合并两个不同长度的数组对象,基于值进行合并

3

我需要一些关于编程任务的帮助,这个任务相对直接但对我来说有挑战性。

有两个数组对象,一个长数组和一个短数组:

var arrayShort = [
  {
    id: 'A',
    name: 'first'
  },{
    id: 'B',
    name: 'second'
  },{
    id: 'C',
    name: 'third'
  }
]

var arrayLong = [
  {
    id: 'A',
    name: 'firstSub1'
  },{
    id: 'A',
    name: 'firstSub2'
  },{
    id: 'A',
    name: 'firstSub3'
  },{
    id: 'B',
    name: 'secondSub1'
  },{
    id: 'B',
    name: 'secondSub2'
  },{
    id: 'B',
    name: 'secondSub3'
  },{
    id: 'C',
    name: 'thirdSub1'
  },{
    id: 'C',
    name: 'thirdSub2'
  },{
    id: 'C',
    name: 'thirdSub3'
  }
]

我该如何根据共享的ID将它们合并在一起,以获得以下结果:
var arrayCombined = [
  {
    id: 'A',
    name: 'first'
  },{
    id: 'A',
    name: 'firstSub1'
  },{
    id: 'A',
    name: 'firstSub2'
  },{
    id: 'A',
    name: 'firstSub3'
  },{
    id: 'B',
    name: 'second'
  },{
    id: 'B',
    name: 'secondSub1'
  },{
    id: 'B',
    name: 'secondSub2'
  },{
    id: 'B',
    name: 'secondSub3'
  },{
    id: 'C',
    name: 'third'
  },{
    id: 'C',
    name: 'thirdSub1'
  },{
    id: 'C',
    name: 'thirdSub2'
  },{
    id: 'C',
    name: 'thirdSub3'
  },
]

重要提示

顺序很重要。

我的尝试

for (var i = 0; i < arrayShort.length; i++) {
  var itemShort = arrayShort[i]
  console.log(itemShort)
  for (var j = 0; j < arrayLong.length; j++) {
        var itemLong = arrayLong[j]
    if (itemLong.id === itemShort.id) {
      console.log(itemLong)
      arrayShort.splice(j + 1, 0, arrayLong)
    }
  }
}


var arrayCombined = arrayShort
console.log(arrayCombined)

这会导致无限循环,但我不确定为什么。一般来说,我知道嵌套循环和条件语句是个坏消息。

有更好的方法(在思维上、代码等方面)来解决这个问题吗?

这是一个 Codepen,打开开发工具查看。

注:

  • 首选 Vanilla Javascript 解决方案,但也可以看到使用 Lodash 或 Underscores 完成此任务的方式。通常,我感觉这两个库都可以使这种类型的任务变得超级容易,但我似乎永远无法准确理解。
  • 尝试找到正确的搜索词对于这个问题来说是出奇的困难... 有什么建议吗?

感谢反馈/见解。


由于两个列表似乎已经排序并且顺序相同,因此请使用 https://en.wikipedia.org/wiki/Merge_algorithm。 - Bergi
arrayShort.splice(j + 1, 0, arrayLong) 是一个不好的想法。最好尝试构建一个新数组,这样更容易和更清晰。 - Bergi
@Bergi 感谢您指向合并算法。是的,我确实看到了在嵌套的结构中进行切割操作是一个不好的方法 - 这也是我提出这个问题的原因。 - Isaac Gregson
此外,值得指出的是这些数据都来自同一个大的 json 数据块,我需要从外部 API 接收并解析成我所需的数据结构。 - Isaac Gregson
如果你期望一个巨大的JSON数据块,那么我认为你不应该采用排序方法。这样做代码量少,但会牺牲性能。 - thefourtheye
2个回答

2

连接和排序是您的好伙伴

var arrayCombined = (arrayShort.concat(arrayLong).sort(function(a,b) {


            return (a.id > b.id) ?  1 : 
                  ((a.id < b.id) ? -1 : 0);

})).sort(function(a,b) {

            return (a.name > b.name) ?  1 : 
                  ((a.name < b.name) ? -1 : 0);

});;

console.log(arrayCombined);

这里是输出结果

[ { id: 'A', name: 'first' },
  { id: 'A', name: 'firstSub1' },
  { id: 'A', name: 'firstSub2' },
  { id: 'A', name: 'firstSub3' },
  { id: 'B', name: 'second' },
  { id: 'B', name: 'secondSub1' },
  { id: 'B', name: 'secondSub2' },
  { id: 'B', name: 'secondSub3' },
  { id: 'C', name: 'third' },
  { id: 'C', name: 'thirdSub1' },
  { id: 'C', name: 'thirdSub2' },
  { id: 'C', name: 'thirdSub3' } ]

1
请注意:这种方法效率极低。由于需要对大数据集进行O(N lg N)排序。 - thefourtheye
在实施这种方法一段时间后,我注意到(并且在我的结果中也看到)secondthird不在它们的列表顶部... 这意味着这种方法不起作用!正如我在问题中提到的那样,顺序很重要。有什么想法吗? - Isaac Gregson
更新:首先按id排序,然后按其他属性排序。 - Scott Stensland

1
使用lodash,您可以像这样做:

var arrayShort = [
    { id: 'A', name: 'first' },
    { id: 'B', name: 'second' },
    { id: 'C', name: 'third' }
];

var arrayLong = [
    { id: 'A', name: 'firstSub1' },
    { id: 'A', name: 'firstSub2' },
    { id: 'A', name: 'firstSub3' },
    { id: 'B', name: 'secondSub1' },
    { id: 'B', name: 'secondSub2' },
    { id: 'B', name: 'secondSub3' },
    { id: 'C', name: 'thirdSub1' },
    { id: 'C', name: 'thirdSub2' },
    { id: 'C', name: 'thirdSub3' }
];

_(arrayShort)
    .union(arrayLong)
    .sortByAll('id', 'name')
    .value()
// →
// [
//   { id: 'A', name: 'first' },
//   { id: 'A', name: 'firstSub1' },
//   { id: 'A', name: 'firstSub2' },
//   { id: 'A', name: 'firstSub3' },
//   { id: 'B', name: 'second' },
//   { id: 'B', name: 'secondSub1' },
//   { id: 'B', name: 'secondSub2' },
//   { id: 'B', name: 'secondSub3' },
//   { id: 'C', name: 'third' },
//   { id: 'C', name: 'thirdSub1' },
//   { id: 'C', name: 'thirdSub2' },
//   { id: 'C', name: 'thirdSub3' }
// ]

其中union()是一种不会在任何一个数组中产生副作用的廉价连接两个数组的方式,而sortByAll()则可让您按多个属性名称进行排序。


+1. 在看到这个之前,我已经采用了同样的方法。为什么?主要是因为我有一些其他关系(子子部分)需要包含,而 Lodash 的 flatten 可以很好地解决这个问题。 - Isaac Gregson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接