javascript - 按键将数组列表的元素分组/连接在一起

标签 javascript arrays angular typescript ramda.js

我有以下数据结构作为输入

我想在不丢失任何数据的情况下对数据进行特殊分组

const input =
[
  {
   Id: 10,
   name: "Test_10",
   points: [{…}, {…}, {…}],
   label: "Label_10",
   level: "1",
   unit: "%"
  },
  {
   Id: 20,
   name: "Test_10",
   points: [{…}, {…}, {…}, {…}],
   label: "Label_10",
   level: "1",
   unit: "%"
  },
  {
   Id: 30,
   name: "Test_10",
   points: [{…}, {…}, {…}],
   label: "Label_10",
   level: "2",
   unit: "°C"
  },
  {
   Id: 40,
   name: "Test_10",
   points: [{…}, {…}, {…}, {…}],
   label: "Label_10",
   level: "2",
   unit: "°C"
  }
]

在我转换/分组后,我想在下面有这个输出。我想将所有点信息与同一个“级别”结合起来。所以我的输出中有“1”级七分。但我只希望它用于我的 1 级项目,而不是 2 级项目。有没有简单的方法可以实现这一目标? (也许吧,但是对于 ramda 或 rxjs 之外的函数来说是必要的吗?)

const output =
[
  {
   Id: 10, // is not important for further code, can be first entry
   name: "XX", // is not important for further code
   points: [{…}, {…}, {…}, {…}, {…}, {…}, {…}], // all points now together
   label: "Label_10",
   level: "1",
   unit: "%"
  },
  {
   Id: 30,
   name: "Test_10",
   points: [{…}, {…}, {…}],
   label: "Label_10",
   level: "2",
   unit: "°C"
  },
  {
   Id: 40,
   name: "Test_10",
   points: [{…}, {…}, {…}, {…}],
   label: "Label_10",
   level: "2",
   unit: "°C"
  }
]

最佳答案

更新

我对下面的原始解决方案不是很满意。这个版本不包含任何依赖项,即使不是很清楚,也是一个相当简单的 ES6 解决方案:

const combine = (a, b) =>
  ({...b, points: a .points .concat (b .points) })
 
const mergeLevel1Points = xs => 
  [xs .filter (x => x .level == '1') .reduce (combine, {points: []}), ...xs .filter(x => x .level != '1')]

 
const input = [
  {Id: 10, name: "Test_10", points: [1, 2, 3], label: "Label_10", level: "1", unit: "%"},
  {Id: 20, name: "Test_10", points: [4, 5, 6, 7], label: "Label_10", level: "1", unit: "%"},
  {Id: 30, name: "Test_10", points: [8, 9], label: "Label_10", level: "2", unit: "°C"},
  {Id: 40, name: "Test_10", points: [10, 11, 12], label: "Label_10", level: "2", unit: "°C"}
]
 
console .log (
  mergeLevel1Points (input)
)

它保留最后一个而不是第一个 level 1 元素的数据。我确信这可以很容易地改变,但问题清楚地表明这并不重要,所以我保留了实现之外的内容。


原解

这是一个 Ramda 解决方案。我不觉得它特别可读,但要求本身很奇怪,所以也许没关系:

const combine = mergeWithKey (
  (key, a, b) => key == 'points' ? concat(a, b) : a
)

const mergeLevel1Points = pipe (
  partition (propEq ('level', '1') ),
  apply (useWith (prepend, [reduce (combine, {})] ))
)  

const input = [
  {Id: 10, name: "Test_10", points: [1, 2, 3], label: "Label_10", level: "1", unit: "%"},
  {Id: 20, name: "Test_10", points: [4, 5, 6, 7], label: "Label_10", level: "1", unit: "%"},
  {Id: 30, name: "Test_10", points: [8, 9], label: "Label_10", level: "2", unit: "°C"},
  {Id: 40, name: "Test_10", points: [10, 11, 12], label: "Label_10", level: "2", unit: "°C"}
]

console .log (
  mergeLevel1Points (input)
)
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>
<script>
const {mergeWithKey, concat, pipe, partition, propEq, apply, useWith, prepend, reduce} = R
</script>

combine 只需要两个项目并主要使用第一个项目,将其 与第二个项目的 组合起来。也可以这样写:

const combine = (a, b) => ({...a, points: (a.points || []).concat(b.points)})

main 函数首先将输入分为匹配 level 1 和不匹配的那些 (partition)。然后它使用 combine (reduce(combine, {})) 将第一组折叠成一个值,并将结果添加到第二组。 apply 只是将我们的两个参数的函数转换为一个接受数组包含这两个参数的函数,以匹配 partition 的输出。

由于使用了useWith,这个版本对我来说有点反感;这是一个非常强大且通常简洁的函数,但它通常会损害可读性。 (如果我要将它用于公共(public)功能,而不是像这里使用的那样匿名使用,那么我可能会向它添加一个 identity [useWith (prepend, [reduce (combine, {}), identity] )], 以便返回的函数正确地报告它的元数。但这是次要的一点。)

不过,这并不是一个糟糕的实现,而且它的优点是简洁。

关于javascript - 按键将数组列表的元素分组/连接在一起,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57806234/

相关文章:

javascript - 获取 id 并添加到数组,从数组 jquery 中删除

angular - 使用 Angular 4 和 Asp.Net Core 2 获取 ValidateAntiForgeryToken

javascript - 如何在 IE11 中添加未定义或引用的属性

javascript - 使用 JQuery 的 Bootstrap 3 Tabs 的外部链接

javascript - VS 2013 WPF Web浏览器启用Javascript

javascript - 在这种情况下,我应该使用大括号 {} 还是方括号 []?

javascript - 检查字符输入时正则表达式的小问题

javascript - 更新 Javascript 对象、嵌套数据 - 仅在不可更新时插入

events - 兄弟组件之间的 Angular 2 事件捕获

angular - 如何配置我的 Angular 应用程序以使用 CLI 进行生产并使用 JIT 进行开发?