如何在一个数组中合并具有不同定义属性的JavaScript对象

1,065 阅读8分钟

合并数组中具有不同定义属性的JavaScript对象

Array.reduce()是合并具有相同键的对象的解决方案,但不一定是属性。

简介

最近在工作中,我遇到了一个有趣的问题:我需要将多个JavaScript对象根据其匹配的键合并到一个数组中。

一开始听起来比较简单,但还有更多的内容,让我无法使用 传播操作符[Object.assign()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign)来组合这些对象。

  1. 这些对象中的每一个都可能共享一个共同的键--这并不能保证,而且
  2. 虽然它们在每个对象中都有相同的属性,但并不是每个对象都定义了这些属性--对象可能有property: value ,也可能是property: undefined

我需要将共享一个共同键的对象的所有属性以及它们的定义值合并到一个新的单一对象中。为了增加难度,我需要它在TypeScript中工作。😅

所以今天,我将告诉你如何使用JavaScript的 reduce()函数来合并数组中两个(或多个)具有不同定义属性的对象。

背景:motion.qoair.qo 事件

对于那些好奇这种事情在现实生活中何时出现的人来说,这里有关于我的情况的更多背景。

我在一家名为物联网(IoT)的初创公司担任软件工程师。 蓝调无线.我们的主要任务是使物联网开发更简单,无论是否有可靠的互联网连接。Blues通过以下方式实现这一目标 记事本 - 预付费的蜂窝设备,可以嵌入到任何物联网设备的 "边缘",将传感器数据以JSON格式传输到安全的云端。 笔记卡.

除了生产这种硬件和提供安全的云连接来发送物联网数据外,我们还建立了软件应用程序,我们的用户可以将他们的设备连接起来,在图表中看到传感器数据。

下面是一个显示各种传感器数据的图表的例子。

我们正在建立的一个网络应用涉及报告两种类型数据的传感器。

  • 空气质量读数,如温度、湿度、传感器电压和压力,以及
  • 运动读数,如当前检测到的运动数和一段时间内检测到的总运动数。

因此,一个传感器产生两种不同的JSON有效载荷,从Notecard发送到Notehub,然后Notehub将它们路由到我们的网络应用程序,在图表中呈现出来。

下面是一些传递到应用程序的事件的例子。

传感器事件示例

正如你所看到的,数组中的前两个对象共享相同的sensorId abc ,但它们有不同的定义属性。

第一个对象有motion 属性(和未定义的air quality 属性),第二个对象有定义的air quality 属性(和未定义的motion 属性),第三个对象有一个完全不同的sensorId ;那是从另一个传感器读取的motion

这让我想到了我的问题:如何将具有相同sensorId 的对象结合起来*,并*从每个对象中获得所有定义的属性。

所以,让我们开始吧。

用一个mergeObject() 函数合并同类对象,并且reduce()

在这篇文章中,我已经说了很多次 "合并 "和 "结合",你可能已经明白了,JavaScript数组函数 [reduce()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce)可能会在这里发挥作用,而且它确实在发挥作用。

在最基本的情况下,reduce() 遍历数组中的每个元素,并将其加入前面所有数值的累加器中,直到最后只返回一个数值。同样的事情也可以用一个对象的数组来做,只是多了一点逻辑。

如果你想了解更全面的解释 reduce(),我鼓励你去看看 Mozilla文档.

他们的例子很好,我仍然经常访问它,了解各种主题。

下面是我想出的TypeScript风味的解决方案,它可以处理一个对象数组,找到有共同键的对象,然后返回一个有这些对象组合的新数组。

好吧,我意识到上面有很多函数,所以解释一下是有必要的。

HasSensorId

HasSensorId 接口是非常直接的:它意味着凡是引用这个接口的地方,对象的必须在属性中拥有一个sensorId 的字符串。

现在让我们逐一走过这些函数的其余部分。

mergeObject()

这个mergeObject() ,是真正进行对象合并的函数。

这个函数接收的AB 对象是具有共同的sensorId 属性的两个对象,而res 变量将是新返回的它们的属性(和值)的组合对象。

发生的第一件事是使用传播操作符将两个对象结合起来,然后只将它们的键提取到自己的数组中,使用 [Object.keys()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign).如果你把样本sensorEvents 数组传给这个函数,Object.keys() 数组就会变成这样。

在提取了键之后,每个键被添加到新的res 对象中作为一个键,如果B 对象在该键处的值存在(B[key]),它就被添加为res 对象的值,否则,A 对象的值就被代替添加(即使它是undefined )。

最终返回的res 对象看起来像这样。

最后,Vany 的引用是 TypeScript 实现的一部分。通过查看代码的其余部分,我们可以看到VHasSensorId 接口的扩展,这意味着任何事件对象都会有sensorId 作为一个属性。而any 意味着传递到函数中的对象可以有任何属性(或没有),所以它很灵活。

好了,进入下一个函数!

reducer()

reducer() 函数很好地利用了JavaScript中的true [Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map)对象 在JavaScript中的应用。它也使用了HasSensorId 接口,确保每个对象都会有一个sensorId 属性。

每个传入函数的event 对象,其sensorId 都变成了一个key 变量。这个previous 变量依靠Map的 [get()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/get) 函数来获取任何具有相同sensorId 密钥的其他Map对象,如果找到了其他对象,在调用mergeObject() 函数时,新的event 会与之结合。如果没有匹配的previous 对象,则将一个空对象传递给mergeObject() ,并且仍然返回一个新的merged 变量。

最后,Map [set()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/set) 函数keymerged 变量添加到groups Map 对象中。

如果我们的三个sensorEvents ,通过这个函数运行,最后出现的groups 对象看起来是这样的。

因此,我们进入这个代码例子的倒数第二部分,reducedEventsIterator

减少的事件迭代器

reducedEventsIterator 变量是我们在sensorEvents 阵列上调用reduce() 的地方。reducer() 函数作为第一个参数被传递,数组中的每个事件都必须经过,一个新的Map() 对象被传递为累加器对象,它们将在最后被聚集到一起。

在这个 [Object.values()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Object/values)方法连接到最后,这个数组看起来是这样的。

在调用.values() 之后,最终的reducedEventsIterator 看起来更像这样。

这给我们带来了最后一步:将这个对象的地图转化为一个适当的JavaScript对象数组(以及由此带来的所有好处--数组更容易操作)。

最终合并的数组:ReducedEvents

可以说是整个教程中最简单的部分,通过将对象的Map传入方法,就可以将其转化为数组。

请注意 Array.from()复制深层对象

如果你在某种类似数组的结构里有深度嵌套的对象,你需要复制。 *Array.from()*可能对你不起作用。它只做浅层复制的数组实例。

因此,如果我们把上面显示的传感器事件的例子传到我们的reducedEventsIterator() 函数中,这就是最后的结果。

这就是你所拥有的:一个具有所有可用属性的新合并对象的数组。

总结

我喜欢做软件工程师的一个原因是我经常能解决所有有趣的问题。最近,当我在工作中建立一个显示物联网传感器数据的网络应用时,我遇到了一个更独特的问题:取一个传感器事件的数组,如果它们属于同一个传感器,就把这些事件合并起来。问题是,所有的事件都有相同的属性,但根据你看的是哪个事件,有一半的属性的值是undefined

因此,我没有使用传播操作符或Object.assign() ,让这些事件合并成一个对象,而是用一些额外的函数和reduce() 方法来发挥更大的创造力。但它还是起作用了--即使有TypeScript在其中。

过几周再来看看--我会写更多关于JavaScript、React、IoT或其他与Web开发相关的东西。

如果你想确保你不会错过我写的文章,请在这里注册我的新闻通讯:https://www.paigeniedringhaus.substack.com

谢谢你的阅读。我希望这个小小的JavaScript对你今后的工作有所帮助--谁会想到合并对象会如此有趣,对吗?

参考资料和更多资源