Merging objects in array – JavaScript

huangapple go评论72阅读模式
英文:

Merging objects in array- javascript

问题

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/

英文:

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/

答案1

得分: 1

以下是翻译好的部分:

问题出在这一行:

for (k in Object.keys(item))

这里的 k 将会获取由 Object.keys 返回的数组中的索引值,即 0、1、2、... 这些不是 item 中的属性,所以下一个 if 条件将会为假,没有任何内容被添加到合并的对象中。

虽然不是问题,但是 k 应该使用 letconst 定义,现在它被隐式定义为全局变量。

为了纠正这个问题,使用 for..of

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

请注意,不需要测试 hasOwnProperty,因为 Object.keys 仅迭代满足此条件的属性。

替代代码

虽然不是您的问题,但值得创建一个以日期为键的映射,并将相应的合并对象关联到它。最初,该对象将为空,但然后您可以再次迭代数据,将项目合并到映射中按日期为键的对象中。

其次,您可以使用 Object.assign 来执行合并:

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);

这是您提供的代码的翻译部分。

英文:

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:

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

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

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

console.log(merged);

<!-- end snippet -->

huangapple
  • 本文由 发表于 2023年5月18日 03:02:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/76275419.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定