简体   繁体   中英

Node.js loop on API calls

I am trying to get few API calls with different parameters. Get the data and convert it into CSV file by day, city highest temp, city lowest temp and cities with rain.

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

I have the following Object with cities and api key:

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:

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.

You may use Promise.all to achieve this.

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.

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. 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:

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:

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();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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