0

I know there are many variants of this question on here, the answers of which I utilized in getting to this step, but I'm not getting the expected result and I'm not sure why.

I started with some dummy data- an array of objects, each containing an ISO date value for a key- date. I had to remove undefined values and then wanted to merge the results based on that date value. The resulting data before the merge looks like this:

[{
  data: {
    hours: ["2"],
    timeIn: ["2023-05-01T18:00:00Z"],
    timeOut: ["2023-05-01T20:00:00Z"],
    type: ["Client"]
  },
  date: "5/1/2023",
  entries: 2
}, {
  data: {
    hours: [2, 2],
    timeIn: ["2023-05-10T18:00:00Z", "2023-05-10T16:00:00Z"],
    timeOut: ["2023-05-10T20:00:00Z", "2023-05-10T18:00:00Z"],
    type: ["Client"]
  },
  date: "5/10/2023",
  entries: 4
}, {
  date: "5/1/2023",
  visits: 2
}, {
  date: "5/10/2023",
  visits: 4
}, {
  date: "5/1/2023",
  miles: 20
}, {
  date: "5/10/2023",
  miles: 40
}]

And based on this answer: https://stackoverflow.com/a/43742999/14330332 I copied it largely straight over:

var merged = [];

cleaned.forEach(function(item) {
  var idx;
  var found = merged.some(function(el, i) {
    idx = el.date === item.date ? i : null;
    return el.date === item.date;
  });
  if (!found) {
    merged.push(item);
  } else if (idx !== null) {
  console.log(Object.keys(item))
    for (k in Object.keys(item)) {
    console.log(k)
      if (item.hasOwnProperty(k)) {
        merged[idx][k] = item[k];
      }
    }
  }
});

But for some reason the objects containing 'visits' and 'miles' props are not being included in the final output:

[{
  data: {
    hours: ["2"],
    timeIn: ["2023-05-01T18:00:00Z"],
    timeOut: ["2023-05-01T20:00:00Z"],
    type: ["Client"]
  },
  date: "5/1/2023",
  entries: 2
}, {
  data: {
    hours: [2, 2],
    timeIn: ["2023-05-10T18:00:00Z", "2023-05-10T16:00:00Z"],
    timeOut: ["2023-05-10T20:00:00Z", "2023-05-10T18:00:00Z"],
    type: ["Client"]
  },
  date: "5/10/2023",
  entries: 4
}]

With the use of hasOwnProperty in the for loop, why would those not be included as well? Being that the idx (date prop) is found and != null, the prop is distinct and should get added to the merge, no? Been staring at this too long now and just not sure what I'm missing.

Heres a link to a jsFiddle I'm playing around in: https://jsfiddle.net/joznox/hyeb2qj4/65/

Jowz
  • 399
  • 1
  • 3
  • 16
  • What should happen when two objects with the same date both have the same properties (other than the date) with different values? – trincot May 17 '23 at 19:08

1 Answers1

1

The problem is in this line:

for (k in Object.keys(item))

Here k will get values of indices in the array that is returned by Object.keys, so 0, 1, 2, ... These are not properties in item, so the next if condition will be false, and nothing gets added to the merged object.

Not the problem, but k should be defined with let or const -- now it is implicitly defined as a global.

To correct this, use for..of:

for (let k of Object.keys(item))

Note that it is not needed to test hasOwnProperty, as Object.keys only iterates properties for which this condition is true.

Alternative code

Not your question, but it may be worth to create a map keyed by date and associate the corresponding merged object to it. Initially that object will be empty, but then you can iterate the data again to merge items into the objects that are keyed by date in the map.

Secondly, you can benefit from Object.assign to perform a merge:

const cleaned = [{data: {hours: ["2"],timeIn: ["2023-05-01T18:00:00Z"],timeOut: ["2023-05-01T20:00:00Z"],type: ["Client"]},date: "5/1/2023",entries: 2}, {data: {hours: [2, 2],timeIn: ["2023-05-10T18:00:00Z", "2023-05-10T16:00:00Z"],timeOut: ["2023-05-10T20:00:00Z", "2023-05-10T18:00:00Z"],type: ["Client"]},date: "5/10/2023",entries: 4}, {date: "5/1/2023",visits: 2}, {date: "5/10/2023",visits: 4}, {date: "5/1/2023",miles: 20}, {date: "5/10/2023",miles: 40}];

const map = new Map(cleaned.map(item => [item.date, {}]));
for (const item of cleaned) Object.assign(map.get(item.date), item);
const merged = [...map.values()];

console.log(merged);
trincot
  • 317,000
  • 35
  • 244
  • 286