Ramda:通过具有关联值的数组进行筛选



这是我的初始数据集:

arr1 = [{
url: ['https://example.com/A.jpg?', 'https://example.com/B.jpg?', 'https://example.com/C.jpg?'], 
width: ['w=300', 'w=400', 'w=500'], 
type: [-1, 1, 2]
}];

通过使用type: n => n > 0进行过滤并将结果传递给arr1,我希望使用Ramda生成arr2。如果第n个值作为滤波器的结果被排除,那么另一个数组中的第n个值也被排除。

arr2 = [{
url: ['https://example.com/B.jpg?', 'https://example.com/C.jpg?'], 
width: ['w=400', 'w=500'], 
type: [1, 2]
}];

我尝试了下面的代码,但不起作用。。。

const isgt0 =  n => n > 0 ;
const arr2 = R.applySpec({
url : arr1,
width : arr1,
type :  R.filter(isgt0),
});
console.log(arr2(arr1));

一旦我得到了想要的对象,我就打算R.transpose数组来生成一个URL,比如:[https://example.com/B.jpg?w=400, https://example.com/C.jpg?w=500]

主要步骤有:

使用R.props:获取值的数组

  • [-1, 1, 2]
  • ['w=300', 'w=400', 'w=500']
  • ['https://example.com/A.jpg?', 'https://example.com/B.jpg?', 'https://example.com/C.jpg?']

将它们转换为具有相同索引的项目数组:

  • [-1, 'w=300', 'https://example.com/A.jpg?']
  • [1, 'w=400', 'https://example.com/B.jpg?']
  • [1, 'w=500', 'https://example.com/C.jpg?']

按索引0(原始type(过滤,转置回去,然后使用R.applySpec.重建对象

const { pipe, props, transpose, filter, propSatisfies, gt, __, tranpose, applySpec, nth, map } = R
const filterProps = pipe(
props(['type', 'width', 'url']), // get an array of property
transpose, // convert to arrays of all property values with the same index
filter(propSatisfies(gt(__, 0), 0)), // filter by the type (index 0)
transpose, // convert back to arrays of each type
applySpec({ // reconstruct the object
type: nth(0),
width: nth(1),
url: nth(2),
})
)
const data = [
{
type: [-1, 1, 2],
width: ['w=300', 'w=400', 'w=500'],
url: [
'https://example.com/A.jpg?', 
'https://example.com/B.jpg?', 
'https://example.com/C.jpg?',
],
}
]
const result = map(filterProps, data)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.js" integrity="sha512-3sdB9mAxNh2MIo6YkY05uY1qjkywAlDfCf5u1cSotv6k9CZUSyHVf4BJSpTYgla+YHLaHG8LUpqV7MHctlYzlw==" crossorigin="anonymous"></script>

另一种更一般地考虑它的方法是使用一个配置对象进行过滤,该配置对象包含应用于各种属性的测试。这里只有type,但很容易想象其他的。

我的这个问题的解决方案是用这个对象配置的:

{
type: n => n > 0
}

该解决方案使用了许多Ramda函数,但也使用Array.prototype.filter来访问filter的索引参数。我们可以选择R.addIndex,但只有当我试图使其无意义时,我才会麻烦,这在这里似乎不值得。这可能是它的样子:

const filterOnProps = (config) => (obj) => {
const test = allPass (map(([k, v]) => (i) => v (obj [k] [i]), toPairs (config)))
const indices = filter (test) (range (0, values (obj) [0] .length))
return map(a => a .filter ((_, i) => contains (i, indices)), obj)
}
const transform =  map (filterOnProps ({type: n => n > 0}))
const arr1 = [{url: ['https://example.com/A.jpg?', 'https://example.com/B.jpg?', 'https://example.com/C.jpg?'], width: ['w=300', 'w=400', 'w=500'], type: [-1, 1, 2]}]
console .log (transform (arr1))
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.min.js"></script>
<script> const {allPass, map, toPairs, filter, range, values, contains} = R    </script>

在作用域中有了obj,我们创建了test,它在某种程度上等同于

allPass([
i => obj['type'][i] > 0
])

如果我们在原始配置对象中有更多的条件,它们也会在这个列表中。

然后我们过滤索引,看看记录在哪些索引上通过了测试。

最后,我们在对象上进行映射,过滤每个数组,只保留那些索引在列表中的数组。


虽然这应该有效,而且相当通用,但它指出了数据结构的问题。我建议你尽可能回避结构依赖于共享指数的情况。在我看来,唯一合理的用途是用于相对紧凑的序列化格式。在反序列化时,我会立即将其重新水合为更有用的东西,也许是之类的东西

const data = [
{url: 'https://example.com/A.jpg?', width: 'w=300', type: -1}, 
{url: 'https://example.com/B.jpg?', width: 'w=400', type: 1},
{url: 'https://example.com/C.jpg?', width: 'w=500', type: 2}
]

这种结构更容易使用。例如,如果您从这个结构开始,data.filter(({type}) => type > 0)将等效于上面的工作。

这可能会对有所帮助

const gte1 = R.filter(R.gte(R.__, 1));
const fn = R.map(
R.evolve({
type: gte1,
}),
);

// =====
const data = [
{
type: [-1, 1, 2],
width: ['w=300', 'w=400', 'w=500'],
url: [
'https://example.com/A.jpg?', 
'https://example.com/B.jpg?', 
'https://example.com/C.jpg?',
],
}
];
console.log(
fn(data),
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.min.js" integrity="sha512-rZHvUXcc1zWKsxm7rJ8lVQuIr1oOmm7cShlvpV0gWf0RvbcJN6x96al/Rp2L2BI4a4ZkT2/YfVe/8YvB2UHzQw==" crossorigin="anonymous"></script>