0

I'm creating a JSON object in NodeJS which will be returned in a GET call. This object must return weather forecast for 3 hours period of each day from the current day.

The JSON desired structure will be:

Desired response

{
    "current": {
        // Stuff... This part works fine
    },
    "forecast": [
        "Tuesday": [
            {
                // More stuff...
            },
            {
                // More stuff...
            },
            // More stuff...
        ],
        "Wednesday": [
            {
                // More stuff...
            },
            {
                // More stuff...
            },
            // More stuff...
        ],
        // More stuff...
    ]
}

In NodeJS I do:

json.forecast = [];
json.forecast[weekDay] = [];

for (var i = 1; i < data.cnt; i++) {
  var hour = {};
  var forecastDay = getDay(forecast[i].dt_txt);

  if (forecastDay !== weekDay) {
    weekDay = forecastDay;
    json.forecast[weekDay] = [];
  }

  hour.date           = forecast[i].dt_txt || 'unknow';          
  hour.day            = getDay(forecast[i].dt_txt);                                    
  hour.time           = getTime(forecast[i].dt_txt);                                   
  hour.temp           = getValue(forecast[i].main.temp);                               
  hour.maxtemp        = getValue(forecast[i].main.temp_max);                           
  hour.mintemp        = getValue(forecast[i].main.temp_min);                           
  hour.wind_speed     = getValue(forecast[i].wind.speed);                              
  hour.rain           = forecast[i].rain ? forecast[i].rain["3h"] : 'unknow';          
  hour.humidity       = getValue(forecast[i].main.humidity);                           
  hour.condition      = {};                                                            
  hour.condition.text = forecast[i].weather[0].description || 'unknow';          
  hour.condition.icon = getIconUrl('openweather', forecast[i].weather[0].icon, logger);

  json.forecast[weekDay].push(hour);
}

The var "weekDay" is generic because I don't know in which day I will start and because I want to create a new item for each day in runtime.

The problem is that every day item pushes into the array but length isn't growing:

NodeJS debug

And the GET response returns an empty array:

Postman response

Andrés Marotta
  • 345
  • 3
  • 21
  • 1
    You need to initiate json.forecast = []; as json.forecast = {}; since its an Object and not an Array. When you do json.forecast[weekDay] = [] it adds the weekDay property to json.forecast, however nothing changes in the json.forecast array that you initially defined – Sandeep Rajoria Sep 17 '19 at 12:44
  • @SandeepRajoria you sir, you are a genius! Post this as an answer so I can choose it as the right one. Thank you! – Andrés Marotta Sep 17 '19 at 12:46

4 Answers4

1

Your forecast is an array but Arrays are indexed data structure. In JS everything is an object hence you are allowed to enter string based properties, however the length will increment for indexed based values. Hence you see length as 0.

Following is a sample illustrating how length works.

Length = Last element's index + 1

In fact, any non indexed value will not even be iterated.

var data = [];

data['foo'] = 'Hello World';

console.log(data.length);

data[10000] = 0;

console.log(data.length);

data.forEach((value, index) => console.log(value, index))
Rajesh
  • 24,354
  • 5
  • 48
  • 79
  • So, what you are saying is that I can "access" or "create" an element with a string index, but I can't populate it if I don't access its numerical index? – Andrés Marotta Sep 17 '19 at 12:44
  • 1
    @AndrésMarotta Objects are ideal for string based keys. Arrays are ideal for numeric keys. You can access them, but as it is not indexed, JS engine does not know how to iterate over it in conventional way and hence you cannot loop over them – Rajesh Sep 17 '19 at 12:46
1

The construction of your forecast array is troublesome.

"forecast": [
    "Tuesday": [
        {
           // More stuff...

It should instead be

"forecast": [
        {
           "day": "Tuesday",
           // More stuff...

or (not ideal)

"forecast": { 
    "Tuesday": [
        {
           // More stuff...

It's not ideal because it's typically bad practice to name an object key by an arbitrary value like "Tuesday".

Zenkylo
  • 136
  • 6
1

Ok, some possible answers:

Try adding another .push() just for the elements inside forecast, without specifying the weekDay. It seems to me that you are adding an index different than a number (incorrectly called string index) to your array, and it is converting the array into an object (with proper length 0). You can take a look here for something similar.

In JavaScript, there is no primitive type of an array. It is an Object with number indexes.

//I said I want an array.
var arr = [];

// But it is an object, with some special proprieties.
console.log(typeof arr) // object

// It is an instance of the Array class. Notice the capitalized 'A' meaning the class.
console.log(arr instanceof Array) //true

// And the reason you know it is an object is that you can add attributes this way.
arr['foo'] = 'bar' 
arr['frodo'] = 'bagging'
arr['ramm'] = 'stein'

// Don't be fooled by [brackets], it is still an object. 
console.log(arr)  // [ foo: 'bar', frodo: 'bagging', ramm: 'stein' ]

// Only arrays have different props like length added by default.
console.log(arr.length) //0

You have 3 alternatives to solve this issue:

  • You can use an array numbered index to assign your values (instead of 'Saturday', 'Monday' strings). Use the array index (Eg: arr[0] to access the first element). Remember that arrays sequence is important, but primitive objects sequence is not.
[{
   data: {...} 
},{
   data: {...} 
}]
  • You can add propriety name or weekDay inside each of your main objects.
[{
   weekDay: 'Monday',
   hour: {...}
},{
   weekDay: 'Tuesday',
   hour: {...}
}]
  • Or you can define the rest of the proprieties inside an object with the name. In modern Javascript, you can loop through proprieties of objects using something like Object.keys(arr)(Take a look) or for..in (Take a look). Remember that you cannot have 2 of the same object keys (two Mondays) if you're using this.
{
   'Monday': { ... },
   'Tuesday': { ... } 
}

dav1app
  • 819
  • 8
  • 7
0

You need to initiate json.forecast = []; as json.forecast = {}; since its an Object and not an Array.

When you do json.forecast[weekDay] = []; it adds the weekDay property to json.forecast, however nothing changes in the json.forecast Array that you initially defined


Additional benefits of using objects

  • No need of this loop: for (var i = 1; i < data.cnt; i++) as you are updating only forecast day's value
  • No need for multiple initialization json.forecast[weekDay] = []; as you can initialize JSON once and update value over and over again.
  • No need for additional checks if (forecastDay !== weekDay)
  • -
Rajesh
  • 24,354
  • 5
  • 48
  • 79
Sandeep Rajoria
  • 1,243
  • 8
  • 14
  • I took the liberty to update your answer. Please review and if you believe anything is not required, please revert/ update it. – Rajesh Sep 17 '19 at 13:01
  • 1
    Thanks @Rajesh You just made it perfect :) Great to have people like you in the community!! – Sandeep Rajoria Sep 17 '19 at 13:02