如何使用 ramda 过滤嵌套数组中的记录?
How to filter records in a nested array using ramda?
我在好几个地方看到过这个问题,但还是想不通。
使用 ramda
,如何将以下对象过滤为 return tomatoes
的 true
记录?
[
{
"id": "a",
"name": "fred",
"food_prefs": {
"tomatoes": true,
"spinach": true,
"pasta": false
},
"country": "singapore"
},
{
"id": "b",
"name": "alexandra",
"food_prefs": {
"tomatoes": false,
"spinach": true,
"pasta": true
},
"country": "france"
},
{
"id": "c",
"name": "george",
"food_prefs": {
"tomatoes": true,
"spinach": false,
"pasta": false
},
"country": "argentina"
}
]
将此数组存储为 myData
对象,我认为以下应该可行:
const R = require("ramda")
const lovesTomatoes = R.pipe ( // taken from:
R.path (["food_prefs"]),
R.filter (R.prop ("tomatoes"))
)
console.log(lovesTomatoes(myData))
但我最终遇到错误:
if (typeof obj[methodNames[idx]] === 'function') {
我做错了什么?
编辑
@Ori Drori 和@ThanosDi 提供的答案都很棒,但我想强调的是,基于 pipe 的解决方案将是理想的,因为我有后续步骤我希望进行过滤阵列。例如考虑以下数组。它与上面的类似,但包含更多数据:year_born
和 year_record
.
[
{
"id": "a",
"name": "fred",
"year_born": 1995,
"year_record": 2010,
"food_prefs": {
"tomatoes": true,
"spinach": true,
"pasta": false
},
"country": "singapore"
},
{
"id": "b",
"name": "alexandra",
"year_born": 2002,
"year_record": 2015,
"food_prefs": {
"tomatoes": false,
"spinach": true,
"pasta": true
},
"country": "france"
},
{
"id": "c",
"name": "george",
"year_born": 1980,
"year_record": 2021,
"food_prefs": {
"tomatoes": true,
"spinach": false,
"pasta": false
},
"country": "argentina"
}
]
所以,例如,要回答一个完整的问题,例如 “对于那些喜欢西红柿的人,记录创建时的平均年龄是多少?”
我们需要:
- 过滤喜欢番茄的记录;
- 提取元素
year_born
和year_record
- 获取值之间的差异
- 取差异的平均值
因此,使用管道将非常有益。
哪里出了问题?
您尝试从数组中获取 food_prefs
的值。由于数组没有此键 - R.path (["food_prefs"])
是 undefined
,然后您尝试过滤此 undefined
值。
如何解决这个问题?
过滤数组,用R.path
得到tomatoes
值
const { filter, path, identity } = R
const lovesTomatoes = filter(path(['food_prefs', 'tomatoes']))
const data = [{"id":"a","name":"fred","food_prefs":{"tomatoes":true,"spinach":true,"pasta":false},"country":"singapore"},{"id":"b","name":"alexandra","food_prefs":{"tomatoes":false,"spinach":true,"pasta":true},"country":"france"},{"id":"c","name":"george","food_prefs":{"tomatoes":true,"spinach":false,"pasta":false},"country":"argentina"}]
const result = lovesTomatoes(data)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.28.0/ramda.min.js" integrity="sha512-t0vPcE8ynwIFovsylwUuLPIbdhDj6fav2prN9fEu/VYBupsmrmk9x43Hvnt+Mgn2h5YPSJOk7PMo9zIeGedD1A==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
使用管道过滤:
使用R.pipe
。对于嵌套属性的简单过滤器,我不会采用这种方式,但您可以使用 Schwartzian transform。思路是创建一个新数组if pairs [value of tomatoes, original object]
,通过tomatoes的值过滤,然后提取原始对象:
const { pipe, map, applySpec, path, identity, filter, last, head } = R
const lovesTomatoes = pipe(
map(applySpec([path(['food_prefs', 'tomatoes']), identity])), // create an array of [value of tomatoes, original object]
filter(head), // filter by the value of the tomatoes
map(last) // extract the original object
)
const data = [{"id":"a","name":"fred","food_prefs":{"tomatoes":true,"spinach":true,"pasta":false},"country":"singapore"},{"id":"b","name":"alexandra","food_prefs":{"tomatoes":false,"spinach":true,"pasta":true},"country":"france"},{"id":"c","name":"george","food_prefs":{"tomatoes":true,"spinach":false,"pasta":false},"country":"argentina"}]
const result = lovesTomatoes(data)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.28.0/ramda.min.js" integrity="sha512-t0vPcE8ynwIFovsylwUuLPIbdhDj6fav2prN9fEu/VYBupsmrmk9x43Hvnt+Mgn2h5YPSJOk7PMo9zIeGedD1A==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
如何在管道中组合第一个lovesTomatoes
过滤函数:
但是,如果您只是需要管道对过滤后的数组执行其他操作,请使用过滤器作为步骤之一:
const { filter, path, identity, pipe, map, prop, uniq } = R
const lovesTomatoes = filter(path(['food_prefs', 'tomatoes']))
const countriesOfTomatoLovers = pipe(
lovesTomatoes,
map(prop('country')),
uniq
)
const data = [{"id":"a","name":"fred","food_prefs":{"tomatoes":true,"spinach":true,"pasta":false},"country":"singapore"},{"id":"b","name":"alexandra","food_prefs":{"tomatoes":false,"spinach":true,"pasta":true},"country":"france"},{"id":"c","name":"george","food_prefs":{"tomatoes":true,"spinach":false,"pasta":false},"country":"argentina"}]
const result = countriesOfTomatoLovers(data)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.28.0/ramda.min.js" integrity="sha512-t0vPcE8ynwIFovsylwUuLPIbdhDj6fav2prN9fEu/VYBupsmrmk9x43Hvnt+Mgn2h5YPSJOk7PMo9zIeGedD1A==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
const myData = [
{
"id": "a",
"name": "fred",
"food_prefs": {
"tomatoes": true,
"spinach": true,
"pasta": false
},
"country": "singapore"
},
{
"id": "b",
"name": "alexandra",
"food_prefs": {
"tomatoes": false,
"spinach": true,
"pasta": true
},
"country": "france"
},
{
"id": "c",
"name": "george",
"food_prefs": {
"tomatoes": true,
"spinach": false,
"pasta": false
},
"country": "argentina"
}
];
const lovesTomatoes = filter(pathOr(false, ['food_prefs','tomatoes']));
lovesTomatoes(myData);
拉姆REPL
Ramda 已经带有一整套谓词built-in,
我在这里使用的其中一个是 pathEq
.
我建议采用 map
和 reduce
类方法,而匹配函数与实际聚合是分开的...
- 收集你的数据点
- 将其缩减为您需要的信息
const tomatoLovers = R.filter(
R.pathEq(['food_prefs', 'tomatoes'], true),
);
const avgAge = R.pipe(R.pluck('age'), R.mean);
const data = [{
"id": "a",
age: 16,
"name": "fred",
"food_prefs": {
"tomatoes": true,
"spinach": true,
"pasta": false
},
"country": "singapore"
},
{
"id": "b",
age: 66,
"name": "alexandra",
"food_prefs": {
"tomatoes": false,
"spinach": true,
"pasta": true
},
"country": "france"
},
{
"id": "c",
age: 44,
"name": "george",
"food_prefs": {
"tomatoes": true,
"spinach": false,
"pasta": false
},
"country": "argentina"
}
]
console.log(
'Average age of tomato lovers is:',
R.pipe(tomatoLovers, avgAge) (data),
);
console.log(
'They are the tomato lovers',
R.pipe(tomatoLovers, R.pluck('name')) (data),
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.28.0/ramda.js" integrity="sha512-ZZcBsXW4OcbCTfDlXbzGCamH1cANkg6EfZAN2ukOl7s5q8skbB+WndmAqFT8fuMzeuHkceqd5UbIDn7fcqJFgg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
我在好几个地方看到过这个问题,但还是想不通。
使用 ramda
,如何将以下对象过滤为 return tomatoes
的 true
记录?
[
{
"id": "a",
"name": "fred",
"food_prefs": {
"tomatoes": true,
"spinach": true,
"pasta": false
},
"country": "singapore"
},
{
"id": "b",
"name": "alexandra",
"food_prefs": {
"tomatoes": false,
"spinach": true,
"pasta": true
},
"country": "france"
},
{
"id": "c",
"name": "george",
"food_prefs": {
"tomatoes": true,
"spinach": false,
"pasta": false
},
"country": "argentina"
}
]
将此数组存储为 myData
对象,我认为以下应该可行:
const R = require("ramda")
const lovesTomatoes = R.pipe ( // taken from:
R.path (["food_prefs"]),
R.filter (R.prop ("tomatoes"))
)
console.log(lovesTomatoes(myData))
但我最终遇到错误:
if (typeof obj[methodNames[idx]] === 'function') {
我做错了什么?
编辑
@Ori Drori 和@ThanosDi 提供的答案都很棒,但我想强调的是,基于 pipe 的解决方案将是理想的,因为我有后续步骤我希望进行过滤阵列。例如考虑以下数组。它与上面的类似,但包含更多数据:year_born
和 year_record
.
[
{
"id": "a",
"name": "fred",
"year_born": 1995,
"year_record": 2010,
"food_prefs": {
"tomatoes": true,
"spinach": true,
"pasta": false
},
"country": "singapore"
},
{
"id": "b",
"name": "alexandra",
"year_born": 2002,
"year_record": 2015,
"food_prefs": {
"tomatoes": false,
"spinach": true,
"pasta": true
},
"country": "france"
},
{
"id": "c",
"name": "george",
"year_born": 1980,
"year_record": 2021,
"food_prefs": {
"tomatoes": true,
"spinach": false,
"pasta": false
},
"country": "argentina"
}
]
所以,例如,要回答一个完整的问题,例如 “对于那些喜欢西红柿的人,记录创建时的平均年龄是多少?”
我们需要:
- 过滤喜欢番茄的记录;
- 提取元素
year_born
和year_record
- 获取值之间的差异
- 取差异的平均值
因此,使用管道将非常有益。
哪里出了问题?
您尝试从数组中获取 food_prefs
的值。由于数组没有此键 - R.path (["food_prefs"])
是 undefined
,然后您尝试过滤此 undefined
值。
如何解决这个问题?
过滤数组,用R.path
得到tomatoes
值
const { filter, path, identity } = R
const lovesTomatoes = filter(path(['food_prefs', 'tomatoes']))
const data = [{"id":"a","name":"fred","food_prefs":{"tomatoes":true,"spinach":true,"pasta":false},"country":"singapore"},{"id":"b","name":"alexandra","food_prefs":{"tomatoes":false,"spinach":true,"pasta":true},"country":"france"},{"id":"c","name":"george","food_prefs":{"tomatoes":true,"spinach":false,"pasta":false},"country":"argentina"}]
const result = lovesTomatoes(data)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.28.0/ramda.min.js" integrity="sha512-t0vPcE8ynwIFovsylwUuLPIbdhDj6fav2prN9fEu/VYBupsmrmk9x43Hvnt+Mgn2h5YPSJOk7PMo9zIeGedD1A==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
使用管道过滤:
使用R.pipe
。对于嵌套属性的简单过滤器,我不会采用这种方式,但您可以使用 Schwartzian transform。思路是创建一个新数组if pairs [value of tomatoes, original object]
,通过tomatoes的值过滤,然后提取原始对象:
const { pipe, map, applySpec, path, identity, filter, last, head } = R
const lovesTomatoes = pipe(
map(applySpec([path(['food_prefs', 'tomatoes']), identity])), // create an array of [value of tomatoes, original object]
filter(head), // filter by the value of the tomatoes
map(last) // extract the original object
)
const data = [{"id":"a","name":"fred","food_prefs":{"tomatoes":true,"spinach":true,"pasta":false},"country":"singapore"},{"id":"b","name":"alexandra","food_prefs":{"tomatoes":false,"spinach":true,"pasta":true},"country":"france"},{"id":"c","name":"george","food_prefs":{"tomatoes":true,"spinach":false,"pasta":false},"country":"argentina"}]
const result = lovesTomatoes(data)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.28.0/ramda.min.js" integrity="sha512-t0vPcE8ynwIFovsylwUuLPIbdhDj6fav2prN9fEu/VYBupsmrmk9x43Hvnt+Mgn2h5YPSJOk7PMo9zIeGedD1A==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
如何在管道中组合第一个lovesTomatoes
过滤函数:
但是,如果您只是需要管道对过滤后的数组执行其他操作,请使用过滤器作为步骤之一:
const { filter, path, identity, pipe, map, prop, uniq } = R
const lovesTomatoes = filter(path(['food_prefs', 'tomatoes']))
const countriesOfTomatoLovers = pipe(
lovesTomatoes,
map(prop('country')),
uniq
)
const data = [{"id":"a","name":"fred","food_prefs":{"tomatoes":true,"spinach":true,"pasta":false},"country":"singapore"},{"id":"b","name":"alexandra","food_prefs":{"tomatoes":false,"spinach":true,"pasta":true},"country":"france"},{"id":"c","name":"george","food_prefs":{"tomatoes":true,"spinach":false,"pasta":false},"country":"argentina"}]
const result = countriesOfTomatoLovers(data)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.28.0/ramda.min.js" integrity="sha512-t0vPcE8ynwIFovsylwUuLPIbdhDj6fav2prN9fEu/VYBupsmrmk9x43Hvnt+Mgn2h5YPSJOk7PMo9zIeGedD1A==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
const myData = [
{
"id": "a",
"name": "fred",
"food_prefs": {
"tomatoes": true,
"spinach": true,
"pasta": false
},
"country": "singapore"
},
{
"id": "b",
"name": "alexandra",
"food_prefs": {
"tomatoes": false,
"spinach": true,
"pasta": true
},
"country": "france"
},
{
"id": "c",
"name": "george",
"food_prefs": {
"tomatoes": true,
"spinach": false,
"pasta": false
},
"country": "argentina"
}
];
const lovesTomatoes = filter(pathOr(false, ['food_prefs','tomatoes']));
lovesTomatoes(myData);
拉姆REPL
Ramda 已经带有一整套谓词built-in,
我在这里使用的其中一个是 pathEq
.
我建议采用 map
和 reduce
类方法,而匹配函数与实际聚合是分开的...
- 收集你的数据点
- 将其缩减为您需要的信息
const tomatoLovers = R.filter(
R.pathEq(['food_prefs', 'tomatoes'], true),
);
const avgAge = R.pipe(R.pluck('age'), R.mean);
const data = [{
"id": "a",
age: 16,
"name": "fred",
"food_prefs": {
"tomatoes": true,
"spinach": true,
"pasta": false
},
"country": "singapore"
},
{
"id": "b",
age: 66,
"name": "alexandra",
"food_prefs": {
"tomatoes": false,
"spinach": true,
"pasta": true
},
"country": "france"
},
{
"id": "c",
age: 44,
"name": "george",
"food_prefs": {
"tomatoes": true,
"spinach": false,
"pasta": false
},
"country": "argentina"
}
]
console.log(
'Average age of tomato lovers is:',
R.pipe(tomatoLovers, avgAge) (data),
);
console.log(
'They are the tomato lovers',
R.pipe(tomatoLovers, R.pluck('name')) (data),
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.28.0/ramda.js" integrity="sha512-ZZcBsXW4OcbCTfDlXbzGCamH1cANkg6EfZAN2ukOl7s5q8skbB+WndmAqFT8fuMzeuHkceqd5UbIDn7fcqJFgg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>