'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?

[
    {
        "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:

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') {

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. Consider for example the following array. It's similar the one above, but includes more data: year_born and 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"
    }
]

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
  3. get the difference between values
  4. take the average of the differences

Therefore, using a pipe would be very beneficial.



Solution 1:[1]

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

Solution 2:[2]

Ramda comes with a whole suite of predicates built-in already, one of them that I'd use here is pathEq.

I'd suggest to adopt a map and reduce kind of approach, whereas the match function is separated from the actual aggregation...

  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>

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 ThanosDi
Solution 2