Home > Back-end >  Group an array of objects by unix time while filtering on nested values
Group an array of objects by unix time while filtering on nested values

Time:09-26

I'm dealing with OpenWeather API. My goal is to display the date and a timetable if that date (at any given time stamp) has windspeed >= MIN_WIND_SPEED

Edit: Added input and expected output JSON

The object returned from the API looks like this:

[
  {
    "dt": 1632808800,
    "wind": {
      "speed": 3.5
    }
  },
  {
    "dt": 1632819600,
    "wind": {
      "speed": 3.53
    }
  },
  {
    "dt": 1632830400,
    "wind": {
      "speed": 3.05
    }
  },
  {
    "dt": 1632841200,
    "wind": {
      "speed": 1.64
    }
  },
  {
    "dt": 1632852000,
    "wind": {
      "speed": 2.39
    }
  },
  {
    "dt": 1632862800,
    "wind": {
      "speed": 3.19
    }
  },
  {
    "dt": 1632873600,
    "wind": {
      "speed": 4.03
    }
  },
  {
    "dt": 1632884400,
    "wind": {
      "speed": 4.67
    }
  },
  {
    "dt": 1632895200,
    "wind": {
      "speed": 3.93
    }
  },
  {
    "dt": 1632906000,
    "wind": {
      "speed": 5.02
    }
  },
  {
    "dt": 1632916800,
    "wind": {
      "speed": 4.86
    }
  },
  {
    "dt": 1632927600,
    "wind": {
      "speed": 3.73
    }
  },
  {
    "dt": 1632938400,
    "wind": {
      "speed": 2.99
    }
  },
  {
    "dt": 1632949200,
    "wind": {
      "speed": 3.31
    }
  },
  {
    "dt": 1632960000,
    "wind": {
      "speed": 4.29
    }
  },
  {
    "dt": 1632970800,
    "wind": {
      "speed": 5.14
    }
  },
  {
    "dt": 1632981600,
    "wind": {
      "speed": 5.86
    }
  },
  {
    "dt": 1632992400,
    "wind": {
      "speed": 6.5
    }
  },
  {
    "dt": 1633003200,
    "wind": {
      "speed": 5.81
    }
  },
  {
    "dt": 1633014000,
    "wind": {
      "speed": 6.58
    }
  },
  {
    "dt": 1633024800,
    "wind": {
      "speed": 4.98
    }
  }
//...
]

I want to convert it into an object like this:

[
  {
    "key": "Saturday 09/25",
    "value": [
      {
        "dt": 1632808800,
        "wind": {
          "speed": 3.5
        }
      },
      {
        "dt": 1632819600,
        "wind": {
          "speed": 3.53
        }
      },
      {
        "dt": 1632830400,
        "wind": {
          "speed": 3.05
        }
      },
      {
        "dt": 1632841200,
        "wind": {
          "speed": 1.64
        }
      },
      {
        "dt": 1632852000,
        "wind": {
          "speed": 2.39
        }
      },
      {
        "dt": 1632862800,
        "wind": {
          "speed": 3.19
        }
      }
    ]
  },
  {
    "key": "Sunday 09/26",
    "value": [
      {
        "dt": 1632808800,
        "wind": {
          "speed": 3.5
        }
      },
      {
        "dt": 1632819600,
        "wind": {
          "speed": 3.53
        }
      },
      {
        "dt": 1632830400,
        "wind": {
          "speed": 3.05
        }
      },
      {
        "dt": 1632841200,
        "wind": {
          "speed": 1.64
        }
      },
      {
        "dt": 1632852000,
        "wind": {
          "speed": 2.39
        }
      },
      {
        "dt": 1632862800,
        "wind": {
          "speed": 3.19
        }
      }
    ]
  },
  {
    "key": "Monday 09/27",
    "value": [
      {
        "dt": 1632808800,
        "wind": {
          "speed": 3.5
        }
      },
      {
        "dt": 1632819600,
        "wind": {
          "speed": 3.53
        }
      },
      {
        "dt": 1632830400,
        "wind": {
          "speed": 3.05
        }
      },
      {
        "dt": 1632841200,
        "wind": {
          "speed": 1.64
        }
      },
      {
        "dt": 1632852000,
        "wind": {
          "speed": 2.39
        }
      },
      {
        "dt": 1632862800,
        "wind": {
          "speed": 3.19
        }
      }
    ]
  }
//...
]

The logic should convert dt into dddd MM/DD and group all the dt of the same day together with key being the date in human readable. It should also remove dates that does not have wind >= MIN_WIND_SPEED

I'm trying with these functions to filter the timestamp that has wind.speed >= MIN_WIND_SPEED and group the entries by key dt

const list = weatherList.filter((i) => i.wind.speed >= MIN_WIND_SPEED);

const groupByDay = () =>
  list.reduce((entryMap, entry) => {
    let selector = unix(entry.dt).format("dddd MM/DD");

    return entryMap.set(selector, [...(entryMap.get(selector) || []), entry]);
  }, new Map());

This method removes all of the time stamps that does not have wind.speed >= MIN_WIND_SPEED. The results have the correct dates but the dates are missing the timestamps with wind.speed < MIN_WIND_SPEED.

Current UI: enter image description here

Expected UI: enter image description here

In this example, Saturday and Sunday should NOT be displayed! Because thay don't have any timestamp that has wind >= MIN_WIND_SPEED

Thanks!

CodePudding user response:

Can't you just but the boolean check inside the reduce function? If it doesn't pass, just return the map? I added a snippet below without the date formatting as it functions the same.

Edit: Added date formatting, not the same way you're doing it, but I believe this is the result you're after. Let me know if I'm wrong.

Edit 2: Updated the code snippet. The date key isn't exactly what you want, but I have to make dinner :P

const speeds = [{
    "dt": 1632808800,
    "wind": {
      "speed": 3.5
    }
  },
  {
    "dt": 1432819600,
    "wind": {
      "speed": 3.53
    }
  },
  {
    "dt": 1632830400,
    "wind": {
      "speed": 3.05
    }
  },
  {
    "dt": 1632841200,
    "wind": {
      "speed": 1.64
    }
  }
]

const groupWindSpeedsByDay = (windSpeeds, minWindSpeed) => {
  const grouped = windSpeeds.reduce((map, windSpeed) => {
    if (windSpeed.wind.speed > minWindSpeed) {
      const date = new Date()
      date.setMilliseconds(windSpeed.dt)
      const selector = `${date.toString().split(' ')[0]} ${date.getMonth()   1}/${date.getDate()}`;
      map[selector] = map[selector] ? [...(map[selector]), windSpeed] : [windSpeed];
    }

    return map
  }, {});

  return Object.keys(grouped).map(key => ({
    key: key,
    values: grouped[key]
  }));
}

console.log(groupWindSpeedsByDay(speeds, 3))

CodePudding user response:

This is just a 'group-by' with a boolean check before accumulating a given element.

Here accumulating into an object using reduce(), and formatting the date using Intl.DateTimeFormat.

const input = [{ dt: 1632808800, wind: { speed: 3.5 }, }, { dt: 1632819600, wind: { speed: 3.53 }, }, { dt: 1632830400, wind: { speed: 3.05 }, }, { dt: 1632841200, wind: { speed: 1.64 }, }, { dt: 1632852000, wind: { speed: 2.39 }, }, { dt: 1632862800, wind: { speed: 3.19 }, }, { dt: 1632873600, wind: { speed: 4.03 }, }, { dt: 1632884400, wind: { speed: 4.67 }, }, { dt: 1632895200, wind: { speed: 3.93 }, }, { dt: 1632906000, wind: { speed: 5.02 }, }, { dt: 1632916800, wind: { speed: 4.86 }, }, { dt: 1632927600, wind: { speed: 3.73 }, }, { dt: 1632938400, wind: { speed: 2.99 }, }, { dt: 1632949200, wind: { speed: 3.31 }, }, { dt: 1632960000, wind: { speed: 4.29 }, }, { dt: 1632970800, wind: { speed: 5.14 }, }, { dt: 1632981600, wind: { speed: 5.86 }, }, { dt: 1632992400, wind: { speed: 6.5 }, }, { dt: 1633003200, wind: { speed: 5.81 }, }, { dt: 1633014000, wind: { speed: 6.58 }, }, { dt: 1633024800, wind: { speed: 4.98 }, },];

const MIN_WIND_SPEED = 4;

const result = Object.values(
  input.reduce((a, o) => {
    if (o.wind.speed >= MIN_WIND_SPEED) {
      const date_string = new Intl.DateTimeFormat('default', {
        weekday: 'long',
        month: '2-digit',
        day: '2-digit',
      }).format(new Date(o.dt * 1000));

      (a[date_string] ??= { key: date_string, value: [] }).value.push({ ...o });
    }

    return a;
  }, {})
);

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Alternatively you could filter() first and then reduce. Here accumulating into a Map, returning an array using Array.from() called on the result of the reduce, and pulling the date formatting out into its own helper function.

const input = [{ dt: 1632808800, wind: { speed: 3.5 }, }, { dt: 1632819600, wind: { speed: 3.53 }, }, { dt: 1632830400, wind: { speed: 3.05 }, }, { dt: 1632841200, wind: { speed: 1.64 }, }, { dt: 1632852000, wind: { speed: 2.39 }, }, { dt: 1632862800, wind: { speed: 3.19 }, }, { dt: 1632873600, wind: { speed: 4.03 }, }, { dt: 1632884400, wind: { speed: 4.67 }, }, { dt: 1632895200, wind: { speed: 3.93 }, }, { dt: 1632906000, wind: { speed: 5.02 }, }, { dt: 1632916800, wind: { speed: 4.86 }, }, { dt: 1632927600, wind: { speed: 3.73 }, }, { dt: 1632938400, wind: { speed: 2.99 }, }, { dt: 1632949200, wind: { speed: 3.31 }, }, { dt: 1632960000, wind: { speed: 4.29 }, }, { dt: 1632970800, wind: { speed: 5.14 }, }, { dt: 1632981600, wind: { speed: 5.86 }, }, { dt: 1632992400, wind: { speed: 6.5 }, }, { dt: 1633003200, wind: { speed: 5.81 }, }, { dt: 1633014000, wind: { speed: 6.58 }, }, { dt: 1633024800, wind: { speed: 4.98 }, },];

const ds = (dt) =>
  new Intl.DateTimeFormat('default', {
    weekday: 'long',
    month: '2-digit',
    day: '2-digit',
  }).format(new Date(dt * 1000));

const MIN_WIND_SPEED = 4;

const result = Array.from(
  input
    .filter((o) => o.wind.speed >= MIN_WIND_SPEED) // filter by MIN_WIND_SPEED
    .map((o) => ({ ds: ds(o.dt), ...o })) // map timestamp to date_string
    .reduce((a, { ds, ...o }) => a.set(ds, [...(a.get(ds) ?? []), o]), new Map()), // reduce

  // 'map' callback provided by Array.from() to refactor Map iterator
  ([key, values]) => ({ key, values })
);

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

  • Related