1

first post!

I've done some searching around and found and tried some answers that were close to my problem, but none of them exactly matched.

I have an array of objects:

const kids = [
  { name: 'alice', 
    age: 13, 
    pets: [
      { type: 'fish', pet_name: 'nemo' },
      { type: 'cat', pet_name: 'snuffles'},
      { type: 'cat', pet_name: 'pixel'}
    ]
  },
  { name: 'bob', 
    age: 7,
    pets: [
      { type: 'dog', pet_name: 'rover' }
    ] 
  },
  { name: 'chris',
    age: 15,
    pets: [
      { type: 'cat', pet_name: 'fluffy' },
      { type: 'donkey', pet_name: 'eeyore' }
    ]
  }
]

I want to get all the kids that own cats, using higher-order functions rather than loops. The result I want is

cat_owners = [ 
              { name: 'alice', age: 13, 
                pets: [ { type: 'cat', pet_name: 'snuffles' },
                        { type: 'cat', pet_name: 'pixel' } ]
              },
              { name: 'chris', age: 15, 
                pets: [ {  type: 'cat', pet_name: 'fluffy' } ] } 
            ]

i've tried various combinations of filter, forEach, find, such as

const cat_owners = kids.filter(kid => kid.pets.find(pet => pet.type === 'cat') )

const kids = [
  { name: 'alice', 
    age: 13, 
    pets: [
      { type: 'fish', pet_name: 'nemo' },
      { type: 'cat', pet_name: 'snuffles'},
      { type: 'cat', pet_name: 'pixel'}
    ]
  },
  { name: 'bob', 
    age: 7,
    pets: [
      { type: 'dog', pet_name: 'rover' }
    ] 
  },
  { name: 'chris',
    age: 15,
    pets: [
      { type: 'cat', pet_name: 'fluffy' },
      { type: 'donkey', pet_name: 'eeyore' }
    ]
  }
];

const cat_owners = kids.filter(kid => kid.pets.find(pet => pet.type === 'cat'));

console.log(cat_owners);

But this also returns alice's fish and chris' donkey - i just want the cats. I've managed to confuse myself silly, and I just can't find the right invocation to get my catlovers and their cats, I've been struggling with this 'simple' problem for a couple of hours now...

so my question is "how do i achieve this desired result?"

thanks in advance

...

EDIT:

thanks to everyone that replied. i managed to do it with the help of the duplicate answer. I had to first filter the kids for pet_type == 'cat', then mapped each kid to a newKid if they had a cat, then filter by pet_type again:

const cat_owners = kids.filter(
                     kid => kid.pets.some(pet => pet.type === 'cat')
                   )
                  .map(kid => {
                    let newKid = Object.assign( {}, kid )
                    newKid.pets = newKid.pets.filter(pet => pet.type == 'cat')
                    return newKid
                  })

this code works, and I'm happy and much less confused.

i realise that there is more than one way to skin a cat, and i have not tried the suggested answers below.

thanks again

  • The filter is only filtering the `kids` to those who have a cat; since they have other animals, those come along with the kid. What you actually want to do is filter the `kids` *and* their `pets`. – Heretic Monkey Oct 24 '19 at 16:02
  • Welcome to StackOverflow - it's great to hear you've found a solution! For future reference, instead of editing your original question, we prefer if you post your solution as an answer and then accept your own answer. (Yes, this is perfectly fine to do! It helps keep the question/answer format of the site nice and tidy.) – Nick Reed Oct 24 '19 at 22:14
  • i'm new to this site, and i can't see a way to add an answer to my own question. on further reflection, i prefer one of the answers given below to the duplicate question solution – mickey megabyte Oct 25 '19 at 12:39

3 Answers3

0

This would help


kids.filter((x) => x.pets.filter((y) => y.type === 'cat').length > 0);

Ahmetcan Aksu
  • 53
  • 2
  • 10
0

If I understand clearly, you want only kids that have cats, but in their pet list, only the cats.

Here is my proposal:

const kids = [
  { name: 'alice', 
    age: 13, 
    pets: [
      { type: 'fish', pet_name: 'nemo' },
      { type: 'cat', pet_name: 'snuffles'},
      { type: 'cat', pet_name: 'pixel'}
    ]
  },
  { name: 'bob', 
    age: 7,
    pets: [
      { type: 'dog', pet_name: 'rover' }
    ] 
  },
  { name: 'chris',
    age: 15,
    pets: [
      { type: 'cat', pet_name: 'fluffy' },
      { type: 'donkey', pet_name: 'eeyore' }
    ]
  }
];

const owners = [];
const type = 'cat';

kids.forEach(kid => {
  kid.pets = kid.pets.filter(pet => pet.type === type);
  if (kid.pets.length)
   return owners.push(kid);
});

console.log(owners);
guillaumepotier
  • 7,369
  • 8
  • 45
  • 72
0

The crazy one-liner, that kinda works:

const catLovers = kids.map(k => k.pets.some(p => p.type === 'cat') ? {...k, pets: k.pets.filter(p => p.type === 'cat')} : null).filter(k => k)

It implements the following algorithm: "for every kid, that has at least one cat, create new array record with cats only (or null otherwise), then filter out empty records".

Formatting might be added to improve readability, but it won't be a one-liner after that :)

Igor Dymov
  • 16,230
  • 5
  • 50
  • 56