【问题标题】:Filter out unique nested values using ramda使用 ramda 过滤掉唯一的嵌套值
【发布时间】:2018-10-22 04:06:11
【问题描述】:

我有一组 UNIX 时间戳,如下所示:

[
  {"start_time":1540458000000, "end_time":1540472400000}, 
  {"start_time":1540458000000, "end_time":1540486800000}, 
  {"start_time":1540458000000, "end_time":1540501200000}, 
  {"start_time":1540472400000, "end_time":1540486800000}, 
  {"start_time":1540472400000, "end_time":1540501200000}, 
  {"start_time":1540486800000, "end_time":1540501200000}
]

我想从start_time 和end_time 中挑选出所有唯一值,所以我只剩下:

[
  {"start_time":1540458000000}, 
  {"start_time":1540472400000}, 
  {"start_time":1540486800000}
  {"end_time":1540472400000},
  {"end_time":1540486800000}, 
  {"end_time":1540501200000}, 
]

我已经研究过使用groupBy、pluck、zipObj 和更多使用answer here 的东西。但不幸的是没有运气。

一个不错的选择是一个 ramda 函数,它无需指定特定的键即可工作。

【问题讨论】:

  • 那么,获取该输出的逻辑是什么?是“获取所有不同的start_time 值和所有不同的end_time 值,然后将它们放入按值排序的数组中”?
  • @vlaz 没错。
  • @joshbang 看起来 union 和 unionWith 仅在提供两个列表时才有效,而这里我只提供一个。
  • 您可以使用 for in 循环按键将它们分开。创建两个名为 startTimes 和 endTimes 的数组。然后将 union 与两个新数组一起使用。

标签: javascript functional-programming ramda.js


【解决方案1】:

另一种 Ramda 方法:

const {pipe, map, toPairs, unnest, groupBy, head, uniqBy, last, values, apply, objOf} = R

const uniqTimes = pipe(
  map(toPairs),          //=> [[['start', 1], ['end', 2]], [['start', 1], ['end', 3]], ...]
  unnest,                //=> [['start', 1], ['end', 2], ['start', 1], ['end', 3], ...]
  groupBy(head),         //=> {start: [['start', 1], ['start', 1], ['start', 4], ...], end: [['end', 2], ...]}
  map(uniqBy(last)),     //=> {start: [['start', 1], ['start', 4], ...], end: [['end', 2], ...]}
  values,                //=> [[['start', 1], ['start', 4], ...], [['end', 2], ...]]
  unnest,                //=> [['start', 1], ['start', 4], ..., ['end', 2], ...]
  map(apply(objOf))      //=> [{"start": 1}, {"start": 4}, ..., {"end": 2}, ...]
)

const timestamps = [{"start_time":1540458000000,"end_time":1540472400000},{"start_time":1540458000000,"end_time":1540486800000},{"start_time":1540458000000,"end_time":1540501200000},{"start_time":1540472400000,"end_time":1540486800000},{"start_time":1540472400000,"end_time":1540501200000},{"start_time":1540486800000,"end_time":1540501200000}]

console.log(uniqTimes(timestamps))
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.js"></script>

这就是我喜欢使用 Ramda 的方式:构建一个函数管道,每个函数都进行简单的转换。


更新

一条评论询问如何生成更像

的输出
{
   "start_time": [1540458000000, 1540458000000],
   "end_time": [1540472400000, 1540486800000]
}

这应该为相同的输入做到这一点:

const uniqTimes = pipe(
  map(toPairs), 
  unnest,
  groupBy(head),
  map(map(last)),
  map(uniq)
)

【讨论】:

  • 这很好用,一如既往地感谢 Scott。还有一个问题……我如何对所有值进行分组,使其变为:[{"start_time":[1540458000000, 1540458000000],"end_time":[1540472400000, 1540486800000]}] 等?
  • 我确信有一种更简单的方法可以做到这一点,但是用 groupBy(head), map(map(last)) 替换 map(apply(objOf)) 应该可以做到。但如果这是我的目标,我可能会有一个完全不同的管道。
  • 如果您的目标是最后一个,那么这样的内容可能更简洁:pipe(map(toPairs), unnest, groupBy(head), map(map(last)), map(uniq))。
【解决方案2】:

如果您想要的属性未知,但出现在所有对象中,您可以将每个对象转换为对,转置结果数组,获取每个数组的唯一值,将它们取消嵌套为单个数组,然后转换回对象:

const { pipe, map, toPairs, transpose, uniqBy, last, unnest, objOf, apply } = R;

const data = [
  {"start_time":1540458000000, "end_time":1540472400000}, 
  {"start_time":1540458000000, "end_time":1540486800000}, 
  {"start_time":1540458000000, "end_time":1540501200000}, 
  {"start_time":1540472400000, "end_time":1540486800000}, 
  {"start_time":1540472400000, "end_time":1540501200000}, 
  {"start_time":1540486800000, "end_time":1540501200000}
];

const getUniqueProps =
  pipe(
    map(toPairs),
    transpose,
    map(uniqBy(last)),
    unnest,
    map(apply(objOf))
  );
  
console.log(getUniqueProps(data));
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>

如果你知道你想要的属性,你可以groupBy一个属性,从每个组中获取第一个对象,然后从每个对象中挑选你想要的属性:

const { groupBy, prop, concat, pipe, map, head, pick, values } = R;

const data = [
  {"start_time":1540458000000, "end_time":1540472400000}, 
  {"start_time":1540458000000, "end_time":1540486800000}, 
  {"start_time":1540458000000, "end_time":1540501200000}, 
  {"start_time":1540472400000, "end_time":1540486800000}, 
  {"start_time":1540472400000, "end_time":1540501200000}, 
  {"start_time":1540486800000, "end_time":1540501200000}
];

const getUniqueProp = (propName) =>
  pipe(
    groupBy(prop(propName)),
    map(pipe(head, pick([propName]))),
    values,
  );
  
const getStartEnd = (data) => concat(
  getUniqueProp('start_time')(data),
  getUniqueProp('end_time')(data),
);

console.log(getStartEnd(data));
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>

【讨论】:

  • 这很好用!无论属性名称如何,您将如何使其适应工作?
  • getUniqueProp 方法与属性名称无关,因此您可以决定采用哪些道具。但是,如果您想迭代一个对象并收集所有属性,我不确定这是否是解决方案。
  • 这很好。我相信我的解决方案中的map(apply(objOf)) 比您的map(pipe(of, fromPairs)) 更合适,主要是因为fromPairs 是为多属性输出而设计的,而objOf 是专门用于创建单属性对象的。没想到transpose;这是我的解决方案的有用简化。但它确实有一个问题。如果您在某些对象中切换属性顺序,您会得到不同的行为。然而,对于 OP 来说,这可能不是现实世界的问题。
  • 谢谢。我对如何将一对转换为对象感到头疼:)
【解决方案3】:

不确定 ramda,但是下面的普通 js 函数可以做到这一点

const arr = [
  {"start_time":1540458000000, "end_time":1540472400000}, 
  {"start_time":1540458000000, "end_time":1540486800000}, 
  {"start_time":1540458000000, "end_time":1540501200000}, 
  {"start_time":1540472400000, "end_time":1540486800000}, 
  {"start_time":1540472400000, "end_time":1540501200000}, 
  {"start_time":1540486800000, "end_time":1540501200000}
];

function foo(arr) {
  return [...arr.reduce((a, b) => {
    Object.entries(b).forEach(e => a.set(String(e), e));
    return a;
  }, new Map())].map(([_,e]) => ({
  	[e[0]]: e[1]
  }))
}

console.log(foo(arr));

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-06-28
    • 1970-01-01
    • 2018-11-18
    • 2019-12-30
    • 2017-09-06
    • 1970-01-01
    • 2022-07-13
    • 2018-01-30
    相关资源
    最近更新 更多