【问题标题】:How to merge N array of tuples on a common key by functional programming?如何通过函数式编程在一个公共键上合并 N 个元组数组?
【发布时间】:2021-01-16 06:20:30
【问题描述】:

我有 3 个数组,它们都包含元组。第一个值是时间戳 (ts),第二个是当时发生的事件计数。单个数组根据时间戳排序。

var arr1 = [{ts: 1000, val: 1},{ts: 1001, val: 2},{ts: 1002, val: 3}];
var arr2 = [{ts: 1001, val: 4},{ts: 1002, val: 5},{ts: 1005, val: 6}];
var arr3 = [{ts: 1003, val: 8},{ts: 1007, val: 8},{ts: 1008, val: 8}];

我想将它们合并到一个 4xN 数组中的单个时间线中,这样它就变成了:

[
    [1000, 1, 0, 0],
    [1001, 2, 4, 0],
    [1002, 3, 5, 0],
    [1003, 0, 0, 8],
    [1005, 0, 6, 0],
    [1007, 0, 0, 8],
    [1008, 0, 0, 8],
]

其中第一列是时间戳,第二列是第一个数组值,第三列是第二个数组值...

我试图以非功能性的方式进行,但无法提出一个不涉及重复查找数组中的时间戳以查找相应值的简洁明了的解决方案。我觉得应该有一种相对简单的方法可以在功能上做到这一点,因为我基本上是将基于行的数据转换为列数据。

如果解决 N 个数组有问题,我也可以使用 3 个数组的解决方案!

【问题讨论】:

  • 您想用ramda.js 归档非功能性方式吗?我认为您非常确定要发挥作用。
  • 如果它比纯 ES6 javascript 更容易,我已经在我的项目中使用 ramda.js,所以欢迎使用它的解决方案
  • 每个时间戳在给定数组中是否只出现一次?

标签: javascript arrays functional-programming ramda.js


【解决方案1】:

我认为没有什么比您丢弃的在数组中查找时间戳的概念更好的了。如果数据真的很大,那么您可以对其进行索引。但是代码不太可能漂亮。

这是一个 ES6 版本:

const extract = (...xss) => 
  [... new Set (xss .flatMap (xs => xs .map (x => x .ts)))]
    .map (t => [t, ... xss .map (xs => (xs.find(({ts}) => t == ts) || {val: 0}).val)])


const arr1 = [{ts: 1000, val: 1}, {ts: 1001, val: 2}, {ts: 1002, val: 3}];
const arr2 = [{ts: 1001, val: 4}, {ts: 1002, val: 5}, {ts: 1005, val: 6}];
const arr3 = [{ts: 1003, val: 8}, {ts: 1007, val: 8}, {ts: 1008, val: 8}];

console .log (extract (arr1, arr2, arr3))
.as-console-wrapper {max-height: 100% !important; top: 0}

至于 Ramda,我们当然可以在这里使用 Ramda。第一行可以替换为

  uniq (chain (pluck ('ts')) (xss))

我们可以类似地将maps 和find 替换为Ramda 等效项,我们可以使用defaultTo 代替|| {val: 0}。如果您使用 Ramda,您可能可以继续这种方式一段时间,我强烈建议您尝试一下。但我认为,如果您正在寻找一个完全无点的解决方案,它很可能很快就会变得不可读。

【讨论】:

    【解决方案2】:

    扁平化数组,同时将原始数组的索引添加到每个项目。然后,按ts分组,将每个对象数组归约为数字数组,给每个数组添加key(原来的ts),并转换为带有R.values的数组数组:

    const { addIndex, map, pipe, chain, groupBy, prop, reduce, set, lensIndex, mapObjIndexed, values } = R;
    
    const mapIndexed = addIndex(map);
    const fill0 = map(() => 0);
    
    const extract = (...xss) => pipe(
      chain(mapIndexed((o, idx) => ({ ...o, idx }))), // add column index to each object
      groupBy(prop('ts')),
      map(reduce((acc, { idx, val }) => set(lensIndex(idx), val, acc), fill0(xss))), //fill the numbers in the index place
      mapObjIndexed((vals, k) => [k, ...vals]), // create the row array
      values
    )(xss)
    
    
    const arr1 = [{ts: 1000, val: 1}, {ts: 1001, val: 2}, {ts: 1002, val: 3}];
    const arr2 = [{ts: 1001, val: 4}, {ts: 1002, val: 5}, {ts: 1005, val: 6}];
    const arr3 = [{ts: 1003, val: 8}, {ts: 1007, val: 8}, {ts: 1008, val: 8}];
    
    const result = extract(arr1, arr2, arr3);
    
    console.log(result);
    .as-console-wrapper {max-height: 100% !important; top: 0}
    <script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.js" integrity="sha512-3sdB9mAxNh2MIo6YkY05uY1qjkywAlDfCf5u1cSotv6k9CZUSyHVf4BJSpTYgla+YHLaHG8LUpqV7MHctlYzlw==" crossorigin="anonymous"></script>

    使用 vanilla JS,您可以将数组数组缩减为 Map,迭代每个数组的值,如果 Map 中不存在键,则将数组设置为 0,并更新相关索引处的值。使用Array.from() 将 Map 的条目转换为行数组:

    const extract = (...xss) => Array.from(
       xss.reduce((acc, o, i) => {
        o.forEach(({ ts, val }) => { // iterate each array
          if (!acc.has(ts)) acc.set(ts, [...xss].fill(0)); // if the key (ts) doesn't exist set it as an array of 0s
          
          acc.get(ts)[i] = val; // replace the 0 with a value
        });
        
        return acc;
       }, new Map())
    ).map(([k, values]) => [k, ...values]); // convert the Map to an array of arrays
    
    
    const arr1 = [{ts: 1000, val: 1}, {ts: 1001, val: 2}, {ts: 1002, val: 3}];
    const arr2 = [{ts: 1001, val: 4}, {ts: 1002, val: 5}, {ts: 1005, val: 6}];
    const arr3 = [{ts: 1003, val: 8}, {ts: 1007, val: 8}, {ts: 1008, val: 8}];
    
    console .log (extract (arr1, arr2, arr3))
    .as-console-wrapper {max-height: 100% !important; top: 0}

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-01-06
      • 2018-10-31
      • 2017-06-02
      • 1970-01-01
      • 2019-11-11
      • 1970-01-01
      • 2020-04-10
      • 2021-12-22
      相关资源
      最近更新 更多