azad6026
azad6026

Reputation: 139

How to get seperate daily array from the weather API array which gives a 5 days every 3 hours report

Please check if you can help anyone. I have been working on my weather app which I am building on React using axios with openweathermap.org and I am kind of stuck to get data the way I need it. I am using its 5day forecast. It gives you a report with every 3 hours forecast for 5 days.It gives you 40 reports for 5 days. so you get 8 reports for the 3 days in between and for current and the fifth day you get the remaining based on how much time left for current day. This is the API response for the report: report. So you will get this as the reponse:

{
  "data": {
"cod": "200",
"message": 0.0062,
"cnt": 39,
"list": [
  {
    "dt": 1540177200,
    "main": {
      "temp": 24.55,
      "temp_min": 20.88,
      "temp_max": 24.55,
      "pressure": 1008.67,
      "sea_level": 1025.96,
      "grnd_level": 1008.67,
      "humidity": 58,
      "temp_kf": 3.67
    },
    "weather": [
      {
        "id": 800,
        "main": "Clear",
        "description": "clear sky",
        "icon": "02d"
      }
    ],
    "clouds": {
      "all": 8
    },
    "wind": {
      "speed": 3.82,
      "deg": 340.5
    },
    "sys": {
      "pod": "d"
    },
    "dt_txt": "2018-10-22 03:00:00"
  },
  {
    "dt": 1540188000,
    "main": {
      ...
    },
  },
  {
    "dt": 1540198800,
    "main": {
     ...
    },
  },
  {
    "dt": 1540587600,
    "main": {
      . . .
    }
  }
]
}

I just summarised it.I just put in the whole data for the first item and for the rest I put in ... and it is a long array with 40 items. Each one has a unique date timestamp which is "dt" in the beginning of it. I need to get 5 arrays based on a specific day. for that I thought of converting the timestamp(dt) and then get all the items that result in the same "Day". Convert the timestamp like this (using forEach for all items):

 let day ='';
 day = new Date(dt *1000);
 day = day.getDate();

There are two problems when I get converted array:

I want to show the info based on that timestamp for 5 days seperate.

Thanks for your help and ideas everyone.

Upvotes: 2

Views: 3854

Answers (5)

Raxy
Raxy

Reputation: 345

I got an easy way below PS:Don't Skip seeing the code length , I have tried to explain my code in there and that's why it's looking lengthy !


   const daysArray = []; 
   app.post("/", function (req, res) {
   const cityName = req.body.cityName;
   const url =
    "https://api.openweathermap.org/data/2.5/forecast?q=" +
    cityName +
    "&appid={api_key}&units=metric";
    https.get(url, function (response) {
    response.on("data", function (data) {
      const weatherData = JSON.parse(data);

***// The main part starts from below*** 

      const time = new Date().getHours(); 
//1)We got hold of current time(24 hr - format) 
      weatherData.list.forEach(function (single) {
        var textHour = single.dt_txt.substring(11, 13);
//2)We got hold of hour from weatherData
//example "dt_txt": "2020-07-28 21:00:00" ( var textHour= '21')
        var numberHour = parseInt(textHour, 10);
//2) We converted string '21' to int 21 !
        var difference = Math.abs(time - numberHour);
//3) In order to get latest time we finded out the difference 
// i.e. The data from API comes strictly from 00:00 then 3 ,6 , ..,21,24 aka 00:00
// In order to get latest time we finded out the difference 
//example if it was 22:00 the 22(time)-21(numberHour)=1(difference)
        if (
          difference === 1 ||
          difference === 0 ||
          (time === 23 && numberHour === 21) ||
          (time === 24 && numberHour === 0) ||
          (time === 2 && numberHour === 00)
        ) 
//Here in first case 
//If it was time=22 & numberHour=21 then difference=1
//Second Case if time=21 & numberHour=21 then difference =0 
//Third Case if it was time=23 then 23-21=2 and the difference would be 2(Special Case)
//Fourth Case time=24 numberHour=0 , difference =24 (Special Case)
//Fifth Case time=2 numberHour=0:00 , difference = 2 (Special Case)
//NOW FOR ALL THE REST THE DIFFERENCE WOULD ONLY BE '0' AND '1'
{
          daysArray.push(single);
//We pushed the satisfied timings in our daysArray 
        }
      });
      console.log(daysArray);
//BOOM , In console you will be seeing the 5-day data and that too closest to //your current time !
      res.render("forecast", { daysArray: daysArray });
    });
  });
});
 

I am new in this programming world, My first helping hand ! Hope it's up to the mark , Feel free to suggest corrections :)

Upvotes: 1

Speedyankur
Speedyankur

Reputation: 147

    //make a empty map for daily data    
    var dailyData={};

    weatherData.list.map(item => {
       const dateTime = new Date(item.dt * 1000);
       const day = dateTime.getDate();
       const time = dateTime.getHours();
       // check if dailyData map has it
       if(!dailyData[day] )
        dailyData[day]=[];
       dailyData[day].push({...item,day,time});    
    });

Found a simpler solution with 0(n) complexity.

Upvotes: 0

azad6026
azad6026

Reputation: 139

I looked through lots of options and reduce was my answer. I needed to regroup my response based on one value which was the day as I have 5 days so I would have an array grouped by day holding all information for the same day. I also needed to keep my timestamp as I needed the time of the day for that report.

 // get the weather data and recreate an array 
// having day and time(hour in my case) in the new array

        const allData = [];
        Object.entries(weatherInfo).forEach(([key, value]) => {
            const dateTime = new Date(value.dt * 1000);
            const day = dateTime.getDate();
            const time = dateTime.getHours();
            // recreate objects of the array adding time anda day and 
            // keeping timestamp and the rest of the info
            const item = {
                day: day,
                time: time,
                ...value
            };
            allData.push(item);
            return allData;
        });

Then I have a new array which I can use reduce thanks to this answer and get the result that I was looking for:

// Now reduce it to an array grouped by day
    const groupedByDay = "day";
    const groupedArray = allData;
    const groupBy = (groupedArray, groupedByDay) => {
        return groupedArray.reduce((acc, obj) => {
            const key = obj[groupedByDay];
            !acc[key] ? (acc[key] = []) : acc[key].push(obj);
            return acc;
        }, {});
    };
    console.log(groupBy(groupedArray, groupedByDay));

I can simply remove those two const definitions on top and do this:

    const groupBy = (groupedArray, groupedByDay) => {
        return groupedArray.reduce((acc, obj) => {
            const key = obj[groupedByDay];
            !acc[key] ? (acc[key] = []) : acc[key].push(obj);
            return acc;
        }, {});
    };
    console.log(groupBy(allData, 'day'));

Upvotes: 1

Henry Woody
Henry Woody

Reputation: 15652

You can do this by first creating an object to hold reports for each day from the current date to 5 days in the future. Then iterate over the elements of "list" and put each (report) in the correct place in the object.

Here is an example:

Note that response in this code is equal to the data you included in your question.

const today = new Date();
const day = 60 * 60 * 24 * 1000;

const dateBins = {};
const nBins = 6; // there can be reports for up to 6 distinct dates

for (let i = 0; i < nBins; i++) {
    // set up a bin (empty array) for each date
    const date = new Date(today.getTime() + i * day);
    dateBins[date.getDate()] = [];
}

const reports = response.data.list;
for (const report of reports) {
    const reportDate = new Date(report.dt * 1000).getDate();
    dateBins[reportDate].push(report);
}

Changing the declaration of today to new Date('2018-10-21T00:00:00.000Z') will allow this code to work with the specific data you've posted.

Result of dateBins with your posted data:

{ '21':
    [ { dt: 1540177200,
       main: [Object],
       weather: [Array],
       clouds: [Object],
       wind: [Object],
       sys: [Object],
       dt_txt: '2018-10-22 03:00:00' },
     { dt: 1540188000, main: {} } ],
  '22': [ { dt: 1540198800, main: {} } ],
  '23': [],
  '24': [],
  '25': [],
  '26': [ { dt: 1540587600, main: {} } ] }

Upvotes: 1

Pari Baker
Pari Baker

Reputation: 716

//try your code with dt_text format it using this code below and then use that //I tried your code it gabe me undefined

console.log(formatDate("2014-07-23 09:00:00"));
function formatDate(date) {
    let d = new Date(date),
      month = '' + (d.getMonth() + 1),
      day = '' + d.getDate(),
      year = '' + d.getFullYear();
    if (month.length < 2) month = '0' + month;
    if (day.length < 2) day = '0' + day;
    return formattedDate = year + month + day;
  }

Upvotes: 0

Related Questions