Javascript 如何可靠地合并这两个数组中的对象,并以苗条的方式循环生成的数组?

Javascript 如何可靠地合并这两个数组中的对象,并以苗条的方式循环生成的数组?,javascript,svelte,Javascript,Svelte,我有2个对象数组,每个对象都包含我希望合并的信息。这些数组有一个公共密钥: let countries = [ { code: "AF", name: "Afghanistan" }, { code: "AL", name: "Albania" }, { code: "IL", name: "Israel" }, ] let dial = [

我有2个对象数组,每个对象都包含我希望合并的信息。这些数组有一个公共密钥:

let countries = [
    { code: "AF", name: "Afghanistan" },
    { code: "AL", name: "Albania" },
    { code: "IL", name: "Israel" },
]

let dial = [
    { code: "AF", dial_code: "+93" },
    { code: "AL",  dial_code: "+355" },
    { code: "IL", dial_code: "+972" },
]
我通过以下方式将上述阵列合并为一个新阵列:

let merged = countries.map((item, i) => Object.assign({}, item, dial[i]));
然后,我使用Svelte在表格中显示信息:

<table class="table table-bordered">
  <thead>
    <tr>
      <th>Name</th>
      <th>Code</th>
      <th>Dial Code</th>
    </tr>
  </thead>
  <tbody>
    {#if countries.length}
    {#each merged as m} 
    <tr>
      <td>{m.name}</td>
      <td>{m.code}</td>
      <td>{m.dial_code}</td>
    </tr>
    {/each}
    {:else}
    <tr>
      <td colspan="3">There are no countries</td>
    </tr>
    {/if}
  </tbody>
</table>

一种方法是使用
reduce
,将两个数组合并到一个数组中,然后获取它的Object.value。以下是一个例子:

var-dial=[{code:“IL”,dial_代码:“+972”},{code:“AF”,dial_代码:“+93”},{code:“AL”,dial_代码:“+355”},];
var国家=[{代码:“AF”,名称:“阿富汗”},{代码:“AL”,名称:“阿尔巴尼亚”},{代码:“IL”,名称:“以色列”}];
var结果=对象。值([…拨号,…国家])。减少((acc,elem)=>{
acc[elem.code]={…(acc[elem.code]|{}),…elem};
返回acc;
},{}));

控制台日志(结果)这里是不同的方法

let countries=[
{代号:“AF”,名称:“阿富汗”},
{代号:“AL”,名称:“阿尔巴尼亚”},
{代码:“IL”,名称:“以色列”},
]
让我们拨号=[
{代码:“AF”,拨号代码:“+93”},
{代码:“AL”,拨号代码:“+355”},
{代码:“IL”,拨号代码:+972},
{代码:“ILi”,拨号代码:+972}
]
结果=[]
每小时拨号((x)=>{
countries.forEach(o=>{
如果(x.code==o.code)
返回result.push(Object.assign({},o,x))
})
})

console.log(result)
这里的一些答案基本上使用嵌套循环(在
forEach
中使用
forEach
,或者在
映射中使用
find
)。在这种情况下,这可能很好,但是这种方法的性能是O(n^2),这是一个危险信号。如果有第三个数据数组要用类似的方法合并,它将变成O(n^3),依此类推

更具可扩展性的解决方案是使用查找:

const sources=[国家/地区,拨号];//加上任何你在未来添加!
const codes=new Set(//使用Set是一种简单的重复数据消除方法
sources.map(source=>source.map(d=>d.code)).flat()
);
const lookups=sources.map(source=>{
const lookup=新映射();
forEach(d=>lookup.set(d.code,d));
返回查找;
});
const combined=Array.from(代码,代码=>{
返回Object.assign({},…lookups.map(d=>d.get(code)|{}));
});

我认为排序比内置数据结构更有效

let countries = [
  { code: "AF", name: "Afghanistan" },
  { code: "AL", name: "Albania" },
  { code: "IL", name: "Israel" },
]

let dial = [
  { code: "AF", dial_code: "+93" },
  { code: "AL",  dial_code: "+355" },
  { code: "IL", dial_code: "+972" },
]

const combine = (arr1, arr2) => {
  const combined = arr1.concat(arr2)
  combined.sort((x, y) => x.code.localeCompare(y.code))
  const result = [combined[0]]
  for (let i = 1; i < combined.length; i++) {
    if (combined[i].code == combined[i - 1].code) {
      result[result.length - 1] = { ...result[result.length - 1], ...combined[i] }
    } else {
      result.push(combined[i])
    }
  }

  return result
}

console.log(combine(countries, dial))
let countries=[
{代号:“AF”,名称:“阿富汗”},
{代号:“AL”,名称:“阿尔巴尼亚”},
{代码:“IL”,名称:“以色列”},
]
让我们拨号=[
{代码:“AF”,拨号代码:“+93”},
{代码:“AL”,拨号代码:“+355”},
{代码:“IL”,拨号代码:+972},
]
const combine=(arr1,arr2)=>{
const combled=arr1.concat(arr2)
组合.sort((x,y)=>x.code.localeCompare(y.code))
常量结果=[组合[0]]
for(设i=1;i
哪种方法在两个数组长度不相等的情况下仍然有效?在这种情况下,哪种数组是新长度的定义数组?@RazvanZamfir两者都有效。在
reduce
的情况下,如果拨号代码不存在,则不会在对象中插入密钥,如果使用second,则密钥将存在,但具有未定义的值。你最好用一些虚拟数据在你的计算机上测试它。选择一个,非常漂亮的解决方案。但是,我已经对您的解决方案与嵌套的foreach进行了性能测试,至少在这种情况下,该解决方案比嵌套的foreach慢,而且在数组长度不相同的情况下,它似乎会添加不需要的数据,但是,尽管这种方法可以很容易地修改,以使用出现在两个源或其中一个源中的所有代码集,但这可能是可以避免的。重新表演:是的,很明显,当你有三个项目时,设置最少的技术将获胜。但这不是一件值得优化的事情。这里的方法是O(n),而在最佳情况下,
forEach
方法是O(n^2)。所讨论的数据是一个国家列表,其中约有200个国家——有了这些数据量,O(n)算法的速度已经是naive
forEach
方法的几倍。用(比如)10000个项目,O(n ^ 2)是灾难性的慢。谢谢你的评论,我同意这个解决方案是可扩展的,它对于大数据肯定更快,但是你不认为你的解决方案中的最后一行是嵌套循环吗?因为您正在使用
Array.from
遍历键,并且在键内使用
map
遍历
map
,它们都会复制数组,因为Array.from内部机制也使用
map
?是否可以避免嵌套Array.from和map?@Sven.hig,区别在于
…lookups.map(…)
在2次查找上循环,而不是在n行数据上循环。这就是为什么这是O(n)而不是O(n^2)。另外,我非常怀疑
Array.from
是否在内部使用
map
;没有理由这样做如果我希望
结果
数组只包含
名称
拨号代码
属性,该怎么做?
let countries = [
  { code: "AF", name: "Afghanistan" },
  { code: "AL", name: "Albania" },
  { code: "IL", name: "Israel" },
]

let dial = [
  { code: "AF", dial_code: "+93" },
  { code: "AL",  dial_code: "+355" },
  { code: "IL", dial_code: "+972" },
]

const combine = (arr1, arr2) => {
  const combined = arr1.concat(arr2)
  combined.sort((x, y) => x.code.localeCompare(y.code))
  const result = [combined[0]]
  for (let i = 1; i < combined.length; i++) {
    if (combined[i].code == combined[i - 1].code) {
      result[result.length - 1] = { ...result[result.length - 1], ...combined[i] }
    } else {
      result.push(combined[i])
    }
  }

  return result
}

console.log(combine(countries, dial))