使用Ramda过滤出唯一的嵌套值

5

我有一个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_timeend_time 中提取出所有唯一的值,以便留下:

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

我曾经尝试使用 groupBypluckzipObj 等方式来实现类似的功能,参考了这里的答案,但遗憾的是没有成功。

最好的情况是有一个 Ramda 函数,可以在不需要给出特定键名的情况下就能运行。


那么,获取该输出的逻辑是什么?是“获取所有不同的start_time值和所有不同的end_time值,然后将它们放入按值排序的数组中”吗? - VLAZ
像这样的东西?https://ramdajs.com/docs/#union - joshbang
@vlaz 没错。 - gosseti
@joshbang 看起来 unionunionWith 只能处理两个列表,而我这里只提供了一个。 - gosseti
你可以通过for循环按键将它们拆分成两个数组。创建名为startTimes和endTimes的两个数组。然后使用两个新数组的union。 - joshbang
3个回答

5

另一种 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]}]等等? - gosseti
我相信有更简单的方法来实现这个,但是用 groupBy(head), map(map(last)) 替换 map(apply(objOf)) 应该可以做到。但如果我的目标是这样的话,我可能会有一个完全不同的流程。 - Scott Sauyet
如果这是你的目标,那么像这样写可能会更简洁:pipe(map(toPairs), unnest, groupBy(head), map(map(last)), map(uniq)) - Scott Sauyet

2

不确定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));


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>


这个很好用!你如何使其适应不同的属性名称? - gosseti
getUniqueProp 方法对属性名称不加区分,因此您可以决定要获取哪些属性。但是,如果您想迭代一个对象并收集所有属性,我不确定这是否是解决方案。 - Ori Drori
1
这很不错。我认为我的解决方案中的 map(apply(objOf)) 比你的 map(pipe(of, fromPairs)) 更合适,主要是因为 fromPairs 是为多属性输出而设计的,而 objOf 则专门用于创建单属性对象。我没有想到 transpose;这是从我的解决方案中简化的有用方法。但它确实存在一个问题。如果在某些对象中切换属性顺序,则会得到不同的行为。然而,这可能不是 OP 的真实世界问题。 - Scott Sauyet
谢谢。我一直在为如何将单个键值对转换为对象而苦恼。 - Ori Drori

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接