简体   繁体   English

如何使用 ramda 过滤嵌套数组中的记录?

[英]How to filter records in a nested array using ramda?

I've seen this question in several places but still can't figure this out.我已经在几个地方看到了这个问题,但仍然无法弄清楚。 Using ramda , how can I filter the following object to return the records that are true for tomatoes ?使用ramda ,如何过滤以下 object 以返回tomatoestrue记录?

[
    {
        "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"
    }
]

Storing this array as myData object, I thought that the following should work:将此数组存储为myData object,我认为以下应该有效:

const R = require("ramda")

const lovesTomatoes = R.pipe ( // taken from: https://stackoverflow.com/a/61480617/6105259
    R.path (["food_prefs"]),
    R.filter (R.prop ("tomatoes"))
)

console.log(lovesTomatoes(myData))

But I end up with the error:但我最终得到了错误:

if (typeof obj[methodNames[idx]] === 'function') { if (typeof obj[methodNames[idx]] === 'function') {

What am I doing wrong?我究竟做错了什么?


EDIT编辑


The answers provided by @Ori Drori and @ThanosDi are both great, but I want to emphasize that a pipe -based solution would be ideal because I have follow-up steps I wish to carry on the filtered array. @Ori Drori 和 @ThanosDi 提供的答案都很棒,但我想强调的是,基于pipe的解决方案将是理想的,因为我有后续步骤我希望继续过滤数组。 Consider for example the following array.例如,考虑以下数组。 It's similar the one above, but includes more data: year_born and year_record .它与上述类似,但包含更多数据: year_bornyear_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"
    }
]

So, for example, to answer a full question such as "for those who love tomatoes, what is the average age at the time of the record creation?"因此,例如,要回答一个完整的问题,例如“对于喜欢西红柿的人来说,创造纪录时的平均年龄是多少?”

we would need to:我们需要:

  1. filter the records that love tomates;过滤喜欢番茄的记录;
  2. extract the elements year_born and year_record提取元素year_bornyear_record
  3. get the difference between values获取值之间的差异
  4. take the average of the differences取差异的平均值

Therefore, using a pipe would be very beneficial.因此,使用 pipe 将非常有益。

What went wrong?什么地方出了错?

You try to get the value of food_prefs out of the array.您尝试从数组中获取food_prefs的值。 Since the array doesn't have this key - R.path (["food_prefs"]) is undefined , and then you try to filter this undefined value.由于数组没有这个键 - R.path (["food_prefs"]) is undefined ,然后你尝试过滤这个undefined的值。

How to solve this problem?如何解决这个问题呢?

Filter the array, and use R. pathSatisfies过滤数组,并使用R. pathSatisfies R. pathSatisfies for each object to check if a nested property conforms to a criteria. R. pathSatisfies为每个 object 检查嵌套属性是否符合标准。 Since the value in this case is a boolean, you can use R.identity as the predicate.由于本例中的值为 boolean,因此您可以使用R.identity作为谓词。

 const { filter, pathSatisfies, identity } = R const lovesTomatoes = filter(pathSatisfies(identity, ['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>

Filtering using a pipe:使用 pipe 过滤:

Using R.pipe .使用R.pipe I wouldn't go this way for a simple filter by nested properties, but you can use a Schwartzian transform .对于嵌套属性的简单过滤器,我不会 go 这种方式,但您可以使用Schwartzian transform The idea is to create a new array if pairs [value of tomato, original object] , filter by the value of tomato, and then extract the original object:这个想法是创建一个新数组 if 对[value of tomato, original object] ,通过番茄的值进行过滤,然后提取原始 object:

 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>

How to combine the 1st lovesTomatoes filtering function in a pipe:如何在 pipe 中组合第一个lovesTomatoes过滤function:

However, if you just need the pipe to perform other operations on the filtered array, use the filter as one of the steps:但是,如果您只需要 pipe 对过滤后的数组执行其他操作,请使用过滤器作为步骤之一:

 const { filter, pathSatisfies, identity, pipe, map, prop, uniq } = R const lovesTomatoes = filter(pathSatisfies(identity, ['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);

Ramda REPL拉姆达REPL

Ramda comes with a whole suite of predicates built-in already, one of them that I'd use here is pathEq . Ramda 已经内置了一整套谓词,我在这里使用的其中一个谓词是pathEq

I'd suggest to adopt a map and reduce kind of approach, whereas the match function is separated from the actual aggregation...我建议采用mapreduce这种方法,而匹配 function 与实际聚合分开......

  1. Collect your data point收集您的数据点
  2. Reduce it to the information you need将其简化为您需要的信息

 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>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM