使用 Ramda 减少对象数组中的多个属性并省略其他属性

Reduce multiple properties in array of array of objects and omit other properties using Ramda

我有这个包含对象的数组:

const data = [
  [
    {
      index: 320,
      blocks: 2,
      value: '31011784785',
      participants: 1222,
      cost: '1286828506'
    },
    {
      index: 319,
      blocks: 0,
      value: '111306385',
      participants: 18,
      cost: '0'
    },
    {
      index: 318,
      blocks: 0,
      value: '14550473',
      participants: 10,
      cost: '0'
    }
  ],
  [
    {
      index: 320,
      blocks: 1,
      value: '7089001673',
      participants: 492,
      cost: '648196615'
    },
    {
      index: 319,
      blocks: 0,
      value: '13551137',
      participants: 8,
      cost: '0'
    },
    {
      index: 318,
      blocks: 0,
      value: '11499815',
      participants: 5,
      cost: '0'
    }
  ],
  [
    {
      index: 320,
      blocks: 1,
      value: '408900161',
      participants: 200,
      cost: '648196615'
    },
    {
      index: 319,
      blocks: 0,
      value: '23551231',
      participants: 10,
      cost: '0'
    },
    {
      index: 318,
      blocks: 0,
      value: '104324219',
      participants: 5,
      cost: '0'
    }
  ]
]

我想创建一个包含对象的数组,这些对象将仅具有属性索引、值和参与者,其中值和参与者将是 3 个数组的总和。例如:

[
  {
      index: 320,
      value: 38509686619,
      participants: 1914,
  },
  {
      index: 319,
      value: 148408753,
      participants: 36,
  },
  {
     ...
  }
]

我还希望值字段是 BigInt。

受此启发我做了一些有用的东西,但是太长太麻烦了。

这个问题与 不同,因为我需要两个对象 属性 值,但我不知道该怎么做。

扁平化后,数组的数组,你应该映射和选择想要的属性并将values转换为数字,然后分组,并将每个组组合成一个对象。

const { mergeWithKey, pipe, flatten, map, pick, evolve, groupBy, prop, reduce, values } = R

// merge deep and combine properties value
const combine = mergeWithKey((k, l, r) => k === 'value' || k === 'participants' ? l + r : r)

const mergeData = pipe(
  flatten, // flatten to a single array
  map(pipe(
    pick(['index', 'value', 'participants']), // pick wanted properties
    evolve({ value: Number }) // convert values to a number
  )),
  groupBy(prop('index')), // group by the name
  map(reduce(combine, {})), // combine each group to a single object
  values, // convert back to array
)

const data = [[{"index":320,"blocks":2,"value":"31011784785","participants":1222,"cost":"1286828506"},{"index":319,"blocks":0,"value":"111306385","participants":18,"cost":"0"},{"index":318,"blocks":0,"value":"14550473","participants":10,"cost":"0"}],[{"index":320,"blocks":1,"value":"7089001673","participants":492,"cost":"648196615"},{"index":319,"blocks":0,"value":"13551137","participants":8,"cost":"0"},{"index":318,"blocks":0,"value":"11499815","participants":5,"cost":"0"}],[{"index":320,"blocks":1,"value":"408900161","participants":200,"cost":"648196615"},{"index":319,"blocks":0,"value":"23551231","participants":10,"cost":"0"},{"index":318,"blocks":0,"value":"104324219","participants":5,"cost":"0"}]]

const results = mergeData(data)

console.log(results)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.min.js" integrity="sha512-rZHvUXcc1zWKsxm7rJ8lVQuIr1oOmm7cShlvpV0gWf0RvbcJN6x96al/Rp2L2BI4a4ZkT2/YfVe/8YvB2UHzQw==" crossorigin="anonymous"></script>

普通 JS 解决方案:

const data = [[{"index":320,"blocks":2,"value":"31011784785","participants":1222,"cost":"1286828506"},{"index":319,"blocks":0,"value":"111306385","participants":18,"cost":"0"},{"index":318,"blocks":0,"value":"14550473","participants":10,"cost":"0"}],[{"index":320,"blocks":1,"value":"7089001673","participants":492,"cost":"648196615"},{"index":319,"blocks":0,"value":"13551137","participants":8,"cost":"0"},{"index":318,"blocks":0,"value":"11499815","participants":5,"cost":"0"}],[{"index":320,"blocks":1,"value":"408900161","participants":200,"cost":"648196615"},{"index":319,"blocks":0,"value":"23551231","participants":10,"cost":"0"},{"index":318,"blocks":0,"value":"104324219","participants":5,"cost":"0"}]];


const result = Object.values(data.flat()
  .reduce((acc, { index, value, participants }) => {
    acc[index] ??= { index, value: 0, participants: 0 };
    acc[index].value += Number(value);
    acc[index].participants += Number(participants);
  
    return acc;
}, {}));

console.log(result);
.as-console-wrapper{min-height: 100%!important; top: 0}

此解决方案(使用 JS 而不是 rambda)尝试使用 BigInt 并将值提供到名为 valueB 的道具中。

const groupAndSum = arr => (
  Object.values(
    arr.flat().reduce(
      (acc, {index, value, participants}) => ({
        ...acc,
        [index]: {
          index,
          valueB: BigInt((acc[index]?.valueB ?? 0)) + BigInt(value), // valueB as big-int
          value: (acc[index]?.value || 0) + +value, // value as a "Number"
          participants: (acc[index]?.participants ?? 0) + +participants
        }
      }),
      {}
    )
  ).map( // to display big-int in the console within stack-snippets
    ({valueB, ...rest}) => ({valueB: valueB.toString(), ...rest})
  )
);

const data = [
  [
    {
      index: 320,
      blocks: 2,
      value: '31011784785',
      participants: 1222,
      cost: '1286828506'
    },
    {
      index: 319,
      blocks: 0,
      value: '111306385',
      participants: 18,
      cost: '0'
    },
    {
      index: 318,
      blocks: 0,
      value: '14550473',
      participants: 10,
      cost: '0'
    }
  ],
  [
    {
      index: 320,
      blocks: 1,
      value: '7089001673',
      participants: 492,
      cost: '648196615'
    },
    {
      index: 319,
      blocks: 0,
      value: '13551137',
      participants: 8,
      cost: '0'
    },
    {
      index: 318,
      blocks: 0,
      value: '11499815',
      participants: 5,
      cost: '0'
    }
  ],
  [
    {
      index: 320,
      blocks: 1,
      value: '408900161',
      participants: 200,
      cost: '648196615'
    },
    {
      index: 319,
      blocks: 0,
      value: '23551231',
      participants: 10,
      cost: '0'
    },
    {
      index: 318,
      blocks: 0,
      value: '104324219',
      participants: 5,
      cost: '0'
    }
  ]
];

console.log(groupAndSum(data));

说明

  • Object.values() 用于从以下操作中提取 resulting-object 的值
  • 首先,输入数组flat倾向于去除嵌套
  • 接下来用.reduce遍历数组,生成一个result-object
  • acc 是 accumulator/aggregator 对象
  • 每个 index 要么作为新键添加到 acc 中,其值为实际对象
  • 如果 index 已经出现在 acc 中,那么 valueparticipants 道具是 summed-up.
  • valueB是一个新的prop,用于存储value
  • BigInt格式

额外的 .map() 用于使 valueB 可在 stack-snippets 上的 console.log 中显示。这在原始代码中可能不需要 - 所以请忽略。

一种 Ramda 方法:

const convert = pipe (
  transpose,
  map (xs => ({
    index: xs [0] .index,
    value: xs .reduce ((a, {value}) => a + Number (value), 0),
    participants: sum (pluck ('participants') (xs))
  }))
) 

const data = [[{index: 320, blocks: 2, value: "31011784785", participants: 1222, cost: "1286828506"}, {index: 319, blocks: 0, value: "111306385", participants: 18, cost: "0"}, {index: 318, blocks: 0, value: "14550473", participants: 10, cost: "0"}], [{index: 320, blocks: 1, value: "7089001673", participants: 492, cost: "648196615"}, {index: 319, blocks: 0, value: "13551137", participants: 8, cost: "0"}, {index: 318, blocks: 0, value: "11499815", participants: 5, cost: "0"}], [{index: 320, blocks: 1, value: "408900161", participants: 200, cost: "648196615"}, {index: 319, blocks: 0, value: "23551231", participants: 10, cost: "0"}, {index: 318, blocks: 0, value: "104324219", participants: 5, cost: "0"}]]

console .log (convert (data))
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.28.0/ramda.min.js"></script>
<script> const {pipe, transpose, map, sum, pluck, reduce, applySpec} = R </script>

我们从 transpose 开始,这使它变得更容易使用格式。然后对于每个新行,我们从第一条记录中获取索引,并对参与者和值求和。

因为 BigInt 在 SO 控制台中显示不佳,我跳过了它们,但变化很小:

-    value: xs .reduce ((a, {value}) => a + Number (value), 0),
+    value: xs .reduce ((a, {value}) => a + BigInt(value), BigInt(0)),

一个point-free版本,如果你有那个癖好,也不会更难:

const convert = pipe (
  transpose,
  map (applySpec ({
    index: pipe (head, prop ('index')),
    value: reduce ((a, {value}) => a + Number (value), 0),
    // value: reduce ((a, {value}) => a + BigInt (value), BigInt (0)),
    participants: pipe (pluck ('participants'), sum)
  }))
)