通过键将数组列表的元素分组/合并在一起

时间:2019-09-05 13:08:28

标签: 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"
  }
]

4 个答案:

答案 0 :(得分:1)

更新

我对下面的原始解决方案不满意。此版本不包含任何依赖关系,并且如果不是十分清晰,则是一个相当简单的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仅使用两项,大部分使用第一种,将其points与第二项结合。也可以这样写:

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

主要功能首先将输入分组为与level 1匹配的输入和与partition不匹配的输入。然后,它使用combinereduce(combine, {}))将第一组折叠为单个值,并将结果附加到第二组。 apply只是将我们两个参数的函数转换为一个接受包含两个参数的数组 的函数,以匹配partition的输出。

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

仍然,这不是一个可怕的实现,它具有简洁的优点。

答案 1 :(得分:1)

net : The syntax of this command is:
At C:\Users\MyID\OneDrive - MyCompany\AD Group DropDown.ps1:34 char:64
+ ... path C:\Temp\RawIDs.txt | ForEach-Object {net user $_ /domain} >> C:\ ...
+                                               ~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (The syntax of this command is::String) [], RemoteException
    + FullyQualifiedErrorId : NativeCommandError

NET USER
[username [password | *] [options]] [/DOMAIN]
         username {password | *} /ADD [options] [/DOMAIN]
         username [/DELETE] [/DOMAIN]
         username [/TIMES:{times | ALL}]
         username [/ACTIVE: {YES | NO}]
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"}
];

const mergeLevelOnePoint = R.pipe(
  R.partition (R.propEq('level', '1')),
  R.zipWith(
    R.call,
    [
      R.pipe(
        R.reduce
          (R.mergeDeepWithKey((key, left, right) => (
            key === 'points'
              ? R.concat(left, right)
              : right
          )))
          ({}),
        R.of,
      ),
      R.identity,
    ],
  ),
  R.unnest,
);

console.log(mergeLevelOnePoint(input));

Ramda中一种更易理解的无点解决方案:

  1. <script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>将输入拆分为R.partition
  2. 将分区输出到[ levelOneEntries, otherLevelEntries ],将其输出到R.zipWith(R.call, [fn1, fn2])[fn1(levelOneEntries), fn2(otherLevelEntries)]个深度合并的条目,串联了​​fn1;而points只是一个身份函数。
  3. fn2将输出R.zipWith,它是Array of Array的一种形式。因此,请使用[[mergedLevelOneEntry], otherLevelEntries]展平为一个数组,即R.unnest

答案 2 :(得分:0)

您可以使用JavaScript的reduce()方法来做到这一点。

function groupByLevel(level) {
  return input.reduce((arr, currentValue) => {

    if (currentValue.level === level) {
      const index = arr.findIndex(item => item.level === level);

      if (index === -1) {
        arr.push(currentValue);
      } else {
        arr[index].points = arr[index].points.concat(currentValue.points);
      }
    } else {
      arr.push(currentValue);
    }

    return arr;
  }, []);
}

演示:

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: [1, 2, 3],
    label: "Label_10",
    level: "2",
    unit: "°C"
  },
  {
    Id: 40,
    name: "Test_10",
    points: [4, 5, 6, 7],
    label: "Label_10",
    level: "2",
    unit: "°C"
  }
]

function groupByLevel(level) {
  return input.reduce((arr, currentValue) => {

    if (currentValue.level === level) {
      const index = arr.findIndex(item => item.level === level);

      if (index === -1) {
        arr.push(currentValue);
      } else {
        arr[index].points = arr[index].points.concat(currentValue.points);
      }
    } else {
      arr.push(currentValue);
    }

    return arr;
  }, []);
}

console.log(groupByLevel("1"));

答案 3 :(得分:0)

使用分组和过滤器 演示

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: [1, 2, 3],
    label: "Label_10",
    level: "2",
    unit: "°C"
  },
  {
    Id: 40,
    name: "Test_10",
    points: [4, 5, 6, 7],
    label: "Label_10",
    level: "2",
    unit: "°C"
  }
]


let levelsGroups;

function filterElement(el){
   const topElement = levelsGroups[el.level]
   if(!topElement){
      levelsGroups[el.level] = el;
      return true;
   }
   topElement.points.concat(el.points);   
   return false;
}

function groupInput() {
    levelsGroups = {};
    return input.filter(filterElement);
}
console.log(groupInput());