-3

So I have a problem here that I have a list of objects with an ID and time. So what I did here was trying to loop out each of these results and push a number based on the time that I set. It would also push 0 if the time does not equal of the hour.

let formatted = [];

const results = [
  { id: 'id1', time: 4 },
  { id: 'id1', time: 5 },
  { id: 'id2', time: 1 },
  { id: 'id2', time: 15 },
  { id: 'id2', time: 12 },
  { id: 'id3', time: 6 },
  { id: 'id3', time: 8 },
];

const hours = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, ];

results.forEach((result) => {
  let hourArr = [];

  hours.forEach((hour) => {
    hourArr.push(result.time == hour ? hour : 0);
  });

  formatted.push({ id: result.id, time: hourArr });
});

console.log(formatted);

Output:

[
  {
    id: 'id1',
    time: [
      0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    ],
  },
  {
    id: 'id1',
    time: [
      0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    ],
  },
  {
    id: 'id2',
    time: [
      0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    ],
  },
  {
    id: 'id2',
    time: [
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 0,
    ],
  },
  {
    id: 'id2',
    time: [
      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    ],
  },
  {
    id: 'id3',
    time: [
      0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    ],
  },
  {
    id: 'id3',
    time: [
      0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    ],
  },
];

The problem here is that every iteration of the object only inserts as a new object instead of combining the existing one.

What I want for the output:

[
  {
    id: 'id1',
    time: [
      0, 0, 0, 0, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    ],
  },
  {
    id: 'id2',
    time: [
      0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 0,
    ],
  },
  {
    id: 'id3',
    time: [
      0, 0, 0, 0, 0, 0, 6, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    ],
  },
];
pilchard
  • 12,414
  • 5
  • 11
  • 23
Irving Real
  • 25
  • 1
  • 8
  • btw, what do you with time zero? why do you have such strenge format? if really an array for times, take a boolean value or an array with only times you have. – Nina Scholz Feb 26 '23 at 12:04

1 Answers1

0

This is a basic 'group by' on the id property, but instead of pushing your time values you can assign the index of the newly added time array using each objects value. (I question the value of doing this since you haven't really gained anything and are storing the same data using more space, but nevertheless...)

const input = [{ id: 'id1', time: 4, }, { id: 'id1', time: 5, }, { id: 'id2', time: 1, }, { id: 'id2', time: 15, }, { id: 'id2', time: 12, }, { id: 'id3', time: 6, }, { id: 'id3', time: 8, },];

const result = input.reduce(
  (a, o) => {
    if (!a[o.id]) {
      a[o.id] = { ...o, time: Array(24).fill(0) };
      a._.push(a[o.id]);
    }
    a[o.id].time[o.time] = o.time;
    return a;
  },
  { _: [] }
)._;

console.log(result);

Or using Object.values instead of a referential accumulator

const input = [{ id: 'id1', time: 4, }, { id: 'id1', time: 5, }, { id: 'id2', time: 1, }, { id: 'id2', time: 15, }, { id: 'id2', time: 12, }, { id: 'id3', time: 6, }, { id: 'id3', time: 8, },];

const result = Object.values(
  input.reduce((a, o) => {
    (a[o.id] ??= { ...o, time: Array(24).fill(0) }).time[o.time] = o.time;
    return a;
  }, {})
);

console.log(result);

Here is the abstracted assignment logic for illustration purposes

const object = { time: Array(24).fill(0) };
const time = 4;

object.time[time] = time; // a marvel of opaque variable naming

console.log(object);
// { time: [ 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] }

The classic 'group by', simply accumulating the values into an array, would look something like the following

const input = [{ id: 'id1', time: 4, }, { id: 'id1', time: 5, }, { id: 'id2', time: 1, }, { id: 'id2', time: 15, }, { id: 'id2', time: 12, }, { id: 'id3', time: 6, }, { id: 'id3', time: 8, },];

const result = Object.values(
  input.reduce(
    (a, o) => ((a[o.id] ??= { ...o, time: [] }).time.push(o.time), a),
    {}
  )
);

console.log(result);

/*
[
  { id: 'id1', time: [ 4, 5 ] },
  { id: 'id2', time: [ 1, 15, 12 ] },
  { id: 'id3', time: [ 6, 8 ] }
]
*/
pilchard
  • 12,414
  • 5
  • 11
  • 23
  • What does the { _: [] } and the )._; after the reduce function do? – Irving Real Feb 26 '23 at 11:54
  • By using an initial accumulator of the shape ` { _: [] }` we can build the result array by reference during the grouping and access it after the reduce using `)._;`. This allows us to avoid calling `Object.values` on the result, but does add some complexity. – pilchard Feb 26 '23 at 11:56
  • Added an example using `Object.values` – pilchard Feb 26 '23 at 11:58