使用 Ramda 过滤每个内部属性数组

Posted

技术标签:

【中文标题】使用 Ramda 过滤每个内部属性数组【英文标题】:Filter each inner property arrays with Ramda 【发布时间】:2021-10-11 21:38:49 【问题描述】:

我有一个这样的对象:

const arrays = 
  one: [[1, 33, 41], [2, 0, 27], [3, 7, 9], [4, 1, 3]],
  two: [[1, 77, 2], [2, 6, 3], [3, 0, 0], [4, 55, 3]],
  three: [[1, 4, 6], [2, 0, 0], [3, 5, 6], [4, 0, 0]],
;

如您所见,每个第一个数字都是相等的: 1 代表每个第一个 内部数组 2每秒内部数组 3 代表每三个内部数组 等等……

我想根据每个数组的第一个数字过滤 和一些比较器编号,例如[3] 如果我们有一个过滤器编号 [3](小于或等于 3), 想要的结果是:

const arrays = 
  one:   [[1,33,41], [2,0,27], [3,7,9]],
  two:   [[1,77,2],  [2,6,3],  [3,0,0]],
  three: [[1,4,6],   [2,0,0],  [3,5,6]],
;

由于内部数组的所有第一个数字都小于或等于3。 以 4,5... 开头的数组将被忽略。

ramda 以什么方式拥有此功能?

【问题讨论】:

【参考方案1】:

我了解您想使用 Ramda,这不是使用该库的解决方案,但可以实现相同的目的。您可以通过将第一个数组值a[0] 与传递给函数的maxVal 进行比较来创建一个对象from entries,它是filtered

const arrays = 
  one: [[1, 33, 41], [2, 0, 27], [3, 7, 9], [4, 1, 3]],
  two: [[1, 77, 2], [2, 6, 3], [3, 0, 0], [4, 55, 3]],
  three: [[1, 4, 6], [2, 0, 0], [3, 5, 6], [4, 0, 0]],
;

const filterArrays = (arsObj, maxVal) => 
  return Object.fromEntries(Object.entries(arsObj).map(([k, v]) => 
    return [k, v.filter((a) => a[0] <= maxVal)];
  ));


const result = filterArrays(arrays, 3);
console.log(result);

【讨论】:

谢谢@axtck,只是我们尝试“强制”Ramda。它有助于进一步利用编程的功能方法。我有点喜欢它,但有时它会更耗时。【参考方案2】:

我喜欢 Ramda 的 map 函数,因为它可以迭代对象的属性(因此避免了 Object.fromEntriesObject.entries)并将函数应用于每个对象。该函数是filter,它将内部数组作为参数。赋予filter 的函数本身就是gtehead 的组合;获取数组的第一个元素并将其与 3 进行比较:

const arrays =
   one:   [[1, 33, 41], [2, 0, 27], [3, 7, 9], [4,  1, 3]]
  , two:   [[1, 77,  2], [2, 6,  3], [3, 0, 0], [4, 55, 3]]
  , three: [[1,  4,  6], [2, 0,  0], [3, 5, 6], [4,  0, 0]] ;

   map(filter(compose(gte(3), head)), arrays);
// ^   ^      ^       ^       ^
// A   B      C       D       E

//=>  one:   [[ 1, 33, 41], [2, 0, 27], [3, 7, 9]]
//=> , two:   [[ 1, 77,  2], [2, 6,  3], [3, 0, 0]]
//=> , three: [[ 1,  4,  6], [2, 0,  0], [3, 5, 6]] 
映射每个属性(A);每个数组都传递给 filter (B) 每个内部数组都传递给 compose (C) 取每个内部数组的头部(E)并与3比较(D

Scott Christopher 在 cmets 中正确指出,gte 在部分应用时可能会令人困惑。事实上,整个组合都可以用这个简单的 lambda 替换:([x]) =&gt; x &lt;= 3

我也喜欢的替代解决方案:

map(filter(([x]) => x <= 3), arrays);

【讨论】:

R.gte 和朋友不幸因在部分应用时会引起混乱而臭名昭著。也许是主观的,但为了便于阅读,我建议用 lambda 替换提供给R.filter 的参数以实现相同的效果(例如([x]) =&gt; x &lt;= 3 @ScottChristopher 同意 100%。我添加了您建议的替代答案。谢谢;)【参考方案3】:

我完全赞同@customcommander 的方法, 只是想补充一点,您也可以将numerical indexes 传递给R.propSatisfies

const headIs3OrBelow = R.propSatisfies(R.gte(3), 0);
const fn = R.map(R.filter(headIs3OrBelow));

// ===
const data = 
  one: [[1, 33, 41], [2, 0, 27], [3, 7, 9], [4, 1, 3]],
  two: [[1, 77, 2], [2, 6, 3], [3, 0, 0], [4, 55, 3]],
  three: [[1, 4, 6], [2, 0, 0], [3, 5, 6], [4, 0, 0]],
;

console.log(
  fn(data),
);
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.js" integrity="sha512-3sdB9mAxNh2MIo6YkY05uY1qjkywAlDfCf5u1cSotv6k9CZUSyHVf4BJSpTYgla+YHLaHG8LUpqV7MHctlYzlw==" crossorigin="anonymous" referrerpolicy="no-referrer"&gt;&lt;/script&gt;

也同意gte 和其他类似方法很难阅读,因为它们有点像is 3 gte than x 向后阅读...在Haskell 中你可以这样做:


3 `gte` x

普通方法:

const headIs3OrBelow = ([head]) => head <= 3;

const fn = (data) => Object.entries(data).reduce(
  (res, [k, lists]) => ( ...res, [k]: lists.filter(headIs3OrBelow) ),
  ,
);

// ===
const data = 
  one: [[1, 33, 41], [2, 0, 27], [3, 7, 9], [4, 1, 3]],
  two: [[1, 77, 2], [2, 6, 3], [3, 0, 0], [4, 55, 3]],
  three: [[1, 4, 6], [2, 0, 0], [3, 5, 6], [4, 0, 0]],
;

console.log(
  fn(data),
);

【讨论】:

以上是关于使用 Ramda 过滤每个内部属性数组的主要内容,如果未能解决你的问题,请参考以下文章

markdown 使用ramda通过嵌套值过滤对象数组:有时您无权访问后端,并且您希望过滤来自

使用 Ramda 在特定条件下添加对象属性值

针对ramda中的过滤器上的多个条件进行测试

过滤 obj[array[@]] 为 nil 或空的数组

使用 MongoDB 中的文档属性过滤器获取嵌入数组中的扁平文档数组

Vue JS 基于复选框数组过滤结果