简体   繁体   English

API 调用上的 Node.js 循环

[英]Node.js loop on API calls

I am trying to get few API calls with different parameters.我试图用不同的参数进行一些 API 调用。 Get the data and convert it into CSV file by day, city highest temp, city lowest temp and cities with rain.获取数据并将其转换为 CSV 文件,按天、城市最高温度、城市最低温度和有雨的城市。

API example: https://samples.openweathermap.org/data/2.5/forecast?q=M%C3%BCnchen,DE&appid=b6907d289e10d714a6e88b30761fae22 API 示例: https://samples.openweathermap.org/data/2.5/forecast?q=M%C3%BCnchen,DE&appid=b6907d289e10d714a6e88b3076

I have the following Object with cities and api key:我有以下 Object 与城市和 api 键:

const cities = {
    0: ['Jerusalem', 'il'],
    1: ['New York', 'us'],
    2: ['Dubai', 'ae'],
    3: ['Lisbon', 'pt'],
    4: ['Oslo', 'no'],
    5: ['Paris', 'fr'],
    6: ['Berlin', 'de'],
    7: ['Athens', 'gr'],
    8: ['Seoul', 'kr'],
    9: ['Singapore', 'sgp'],
}

const apiKey = "[retracted]";

This is my API call which I want to iterate dynamically, currently I run it only on the first object params and eventually push the info to weather so I can manipulate the data to order it by days(5 first days), then display the city with the highest temp, city with the lowests temp and all the cities with rain:这是我想动态迭代的 API 调用,目前我只在第一个 object 参数上运行它,并最终将信息推送到天气,这样我就可以操纵数据以按天排序(前 5 天),然后显示城市气温最高、气温最低的城市和所有下雨的城市:

request(`http://api.openweathermap.org/data/2.5/forecast?q=${cities[1][0]},${cities[1][1]}&mode=json&appid=${apiKey}`, (error, response, body) => {
    let data = JSON.parse(body);    
    let weather = {
        0: [day, highTemp, lowTemp, rain],
        1: [day, highTemp, lowTemp, rain],
        2: [day, highTemp, lowTemp, rain],
        3: [day, highTemp, lowTemp, rain],
        4: [day, highTemp, lowTemp, rain],
    }
    // day 1
    console.log(data['city']['name']);
    console.log(data['list'][0].dt_txt);
    console.log(data['list'][0].main['temp']);

    // day 2
    console.log(data['city']['name']);
    console.log(data['list'][8].dt_txt);
    console.log(data['list'][8].main['temp']);
    // day 3
    console.log(data['city']['name']);
    console.log(data['list'][16].dt_txt);
    console.log(data['list'][16].main['temp']);
    // day 4
    console.log(data['city']['name']);
    console.log(data['list'][24].dt_txt);
    console.log(data['list'][24].main['temp']);
    // day 5
    console.log(data['city']['name']);
    console.log(data['list'][32].dt_txt);
    console.log(data['list'][32].main['temp']);

});

I've tried to use for loop with key in object but unfortunately it doesn't display the data, cause of error undefined.我尝试在 object 中使用带有键的 for 循环,但不幸的是它不显示数据,错误原因未定义。

You may use Promise.all to achieve this.您可以使用Promise.all来实现此目的。

Promise.all returns a single Promise that resolves when all of the promises passed as an iterable have resolved or when the iterable contains no promises. Promise.all 返回一个单独的 Promise,当所有作为迭代传递的承诺都已解决或迭代不包含任何承诺时,该 ZA5A3F0F287A448982 将解决。

const getData = (url) => {
   return fetch(url)
    .then(data => data.json())
    .then(jsonData => jsonData)
    .catch(err => {
      console.log("Error while resolving the promise for url", url);        
    });  
}

let arr = [1, 2, 4, 5, 6, 7];

const cities = {
    0: ['Jerusalem', 'il'],
    1: ['New York', 'us'],
    2: ['Dubai', 'ae'],
    3: ['Lisbon', 'pt'],
    4: ['Oslo', 'no'],
    5: ['Paris', 'fr'],
    6: ['Berlin', 'de'],
    7: ['Athens', 'gr'],
    8: ['Seoul', 'kr'],
    9: ['Singapore', 'sgp'],
}

const apiKey = "[retracted]";

Promise.all(Object.keys(cities).map(id => {  
  let url = `http://api.openweathermap.org/data/2.5/forecast?q=${cities[id][0]},${cities[id][1]}&mode=json&appid=${apiKey}`;
  return getData(url);
  }))
 .then(results => {

        // results is an array that contains the result of each api call
        // so you can perform the action that you need here..

        results.map(result => {

          console.log(result['city']['name']);

        });

  })
  .catch(err => {
        // Handle the error..
        console.log(err);
  });  

I'd suggest using response-promise-native to allow the use of async / await.我建议使用 response-promise-native 来允许使用 async / await。 This will allow us to iterate through the city list and attach the weather data for each city to the city details (name and country).这将允许我们遍历城市列表并将每个城市的天气数据附加到城市详细信息(名称和国家/地区)。

Once we have this data, we can do the processing you've mentioned, we can get max and min temperatures (take note, the temperatures are in Kelvin, so we'll convert to Celsius.)一旦我们有了这些数据,我们就可以进行您提到的处理,我们可以获得最高和最低温度(请注意,温度以开尔文为单位,因此我们将转换为摄氏度。)

It's important to point out that I'm grouping by local date , if you wish to group by utc date then you should change the line:重要的是要指出我按本地日期分组,如果您希望按utc 日期分组,那么您应该更改该行:

let timeOffset = entry.dt + result.weatherResponse.city.timezone;

to

let timeOffset = entry.dt;

This is a slightly different way of interpreting the data!这是解释数据的一种稍微不同的方式!

I have now updated to group by date the results looks like so:我现在已更新为按日期分组,结果如下所示:

Grouping by local date:按当地日期分组:

Date,Highest Temperature,Lowest Temperature,Cities With Rain
2019-11-01,Dubai,Oslo,"Paris,Berlin"
2019-11-02,Singapore,Oslo,"Lisbon,Paris,Berlin,Singapore"
2019-11-03,Singapore,Oslo,"Lisbon,Paris,Berlin,Athens,Singapore"
2019-11-04,Singapore,Oslo,"Lisbon,Paris,Berlin,Athens"
2019-11-05,Singapore,Oslo,"Lisbon,Paris,Berlin,Singapore"
2019-11-06,Singapore,Oslo,"Paris,Berlin,Singapore"
2019-11-07,Seoul,Seoul,""

Grouping by UTC date:按 UTC 日期分组:

Date,Highest Temperature,Lowest Temperature,Cities With Rain
2019-11-01,Dubai,Oslo,"Paris,Berlin"
2019-11-02,Singapore,Oslo,"Lisbon,Paris,Berlin,Singapore"
2019-11-03,Singapore,Oslo,"Lisbon,Paris,Berlin,Athens,Singapore"
2019-11-04,Singapore,Oslo,"Lisbon,Paris,Berlin,Athens"
2019-11-05,Singapore,Oslo,"Lisbon,Paris,Berlin,Singapore"
2019-11-06,Singapore,Oslo,"Paris,Berlin,Singapore"

The code:编码:

const rp = require("request-promise-native");

const cities = {
    0: ['Jerusalem', 'il'],
    1: ['New York', 'us'],
    2: ['Dubai', 'ae'],
    3: ['Lisbon', 'pt'],
    4: ['Oslo', 'no'],
    5: ['Paris', 'fr'],
    6: ['Berlin', 'de'],
    7: ['Athens', 'gr'],
    8: ['Seoul', 'kr'],
    9: ['Singapore', 'sgp'],
}


async function getWeatherForCities() {
    let results = [];
    for (let [city, countryCode] of Object.values(cities)) {
        console.log(`Getting weather for city: ${city}, country: ${countryCode}...`);
        let weatherResponse = await rp({ url: `http://api.openweathermap.org/data/2.5/forecast?q=${city},${countryCode}&mode=json&appid=${apiKey}`, json: true});
        results.push ({ city, countryCode, list: weatherResponse.list, weatherResponse });
    }

    let summary = results.map(res => {  
        return { city: res.city, countryCode: res.countryCode,
        maxTemperature: getMaxTemperatureCelsius(res.list),
        minTemperature: getMinTemperatureCelsius(res.list),
        totalRainfall: getTotalRainFall(res.list)
    }});

    console.log("Summary (over forecasting interval): ", summary);
    console.log("Result with the highest temperature: ", [...summary].sort((resA, resB) => resB.maxTemperature - resA.maxTemperature)[0]);
    console.log("Result with the lowest temperature: ", [...summary].sort((resA, resB) => resA.minTemperature - resB.minTemperature)[0]);
    console.log("Cities with rain: ", summary.filter(res => res.totalRainfall).map(res => res.city));

    // Group by date (local) and city
    let resultsGroupedByDateAndCity = {};
    results.forEach(result => {
        result.list.forEach(entry => {
            let timeOffset = entry.dt + result.weatherResponse.city.timezone;
            let date = new Date(timeOffset * 1000);
            date.setHours(0,0,0,0);
            let dateKey = date.toISOString().substring(0,10);
            if (!resultsGroupedByDateAndCity[dateKey]) resultsGroupedByDateAndCity[dateKey] = {};
            if (!resultsGroupedByDateAndCity[dateKey][result.city]) resultsGroupedByDateAndCity[dateKey][result.city] = [];
            resultsGroupedByDateAndCity[dateKey][result.city].push(entry);
        });
    });

    // Run through the keys.
    let csvLines = ["Date,Highest Temperature,Lowest Temperature,Cities With Rain"];

    for (let [date, obj] of Object.entries(resultsGroupedByDateAndCity)) {
        let dailySummary = Object.entries(obj).map(([city, dayList]) => {  
            return { city,
            maxTemperature: getMaxTemperatureCelsius(dayList),
            minTemperature: getMinTemperatureCelsius(dayList),
            totalRainfall: getTotalRainFall(dayList)
        }});

        console.log("Details for date " + date + ": ");
        let resultWithHighestTemperature = [...dailySummary].sort((resA, resB) => resB.maxTemperature - resA.maxTemperature)[0];
        let resultWithLowestTemperature = [...dailySummary].sort((resA, resB) => resA.minTemperature - resB.minTemperature)[0];
        let citiesWithRain = dailySummary.filter(res => res.totalRainfall).map(res => res.city);
        console.log("Result with the highest temperature: ", resultWithHighestTemperature);
        console.log("Result with the lowest temperature: ", resultWithLowestTemperature);
        console.log("Cities with rain: ", citiesWithRain);

        csvLines.push([date, resultWithHighestTemperature.city, resultWithLowestTemperature.city, '"' + citiesWithRain.join(",") + '"'].join(","));
    }

    console.log("CSV result:\n", csvLines.join("\n"));
}

function KelvinToCelsius(kelvin) {
    return (kelvin - 273.15);
}

// Return the max temperature for the forecast
function getMaxTemperatureCelsius(responseList) {
    // Get a list of the max temperatures for the forecast.
    const maxTemps = responseList.map(entry => Number(entry.main.temp_max));
    return KelvinToCelsius(Math.max(...maxTemps));
}

// Return the min temperature for the forecast
function getMinTemperatureCelsius(responseList) {
    // Get a list of the min temperatures for the forecast.
    const minTemps = responseList.map(entry => Number(entry.main.temp_min));
    return KelvinToCelsius(Math.min(...minTemps));
}

// Return the total rainfall for the forecast
function getTotalRainFall(responseList) {
    // Get a list of the min temperatures for the forecast.
    const rain = responseList.map(entry => { return entry.rain ? Number(entry.rain["3h"]): 0 });
    return rain.reduce((sum, val) => sum + val, 0)
}

getWeatherForCities();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM