omnomah
omnomah

Reputation: 95

JS filtering array in object property of array of objects

I have js object like this

var continents = [
0: {
       short: 'na',
       countries: [
           {
               name: 'canada'
           },
           {
               name: 'usa'
           },
           //...
       ]

   },
   1: {
       short: 'sa',
       countries: [
           {
               name: 'chile'
           },
           {
               name: 'colombia'
           }
       ]

     },
//...
]

I want to filter this object for matches with country name (contents.countries.name) with some string (example 'col') Example filter function

filter(item => {
    return item.name.toLowerCase().indexOf('col'.toLowerCase()) >= 0;
});

Expecting result:

  1: {
       short: 'sa',
       countries: [
           {
               name: 'colombia'
           }
       ]
     }

Upvotes: 3

Views: 143

Answers (5)

Nina Scholz
Nina Scholz

Reputation: 386868

You could try to filter the countries and if one found, push the outer continent as well to the result set.

function getItems(name) {
    var result = [];

    continents.forEach(function (continent) {
        var countries = continent.countries.filter(function (country) {
            return country.name.startsWith(name);
        });
        if (countries.length) {
            result.push({ short: continent.short, countries: countries });
        }
    });
    return result;
}

var continents = [{ short: 'na', countries: [{ name: 'canada' }, { name: 'usa' }] }, { short: 'sa', countries: [{ name: 'chile' }, { name: 'colombia' }] }];

console.log(getItems('col'));
.as-console-wrapper { max-height: 100% !important; top: 0; }

Upvotes: 0

David Anthony Acosta
David Anthony Acosta

Reputation: 4918

For a loop you could use:

var continents = [
    {
        short: 'na',
        countries: [
            {
                name: 'canada'
            },
            {
                name: 'usa'
            }
        ]
    },
    {
        short: 'sa',
        countries: [
            {
                name: 'chile'
            },
            {
                name: 'colombia'
            }
        ]
    }
];

var results = continents.filter(item => {
    for (let x = 0; x < item.countries.length; x++) {
        if (item.countries[x].name.toLowerCase().indexOf('usa') > -1) {
            return item;
            break;
        }
    }
});

console.log(results);

Working example: https://jsfiddle.net/gugui3z24/fnw5kf0x/2/

Upvotes: 0

Luke Stoward
Luke Stoward

Reputation: 1540

You can use the filter function to return just the items that match a condition. Like so:

const matches = [];
continents.forEach((continent) => {

  const countries = continent.countries.filter((country) => {
    return country.name.includes('col');
  });

  matches.push(...countries);
});

Note: I have used the spread operator ... to flattern the array (avoiding an array of arrays).

Upvotes: 0

Mr. Polywhirl
Mr. Polywhirl

Reputation: 48751

You need to not only filter on the continents, but the countries within them.

This is a two-part filter, as seen below.

var continents = [{
  short: 'na',
  countries: [
    { name: 'canada' },
    { name: 'usa' }
  ]
}, {
  short: 'sa',
  countries: [
    { name: 'chile' },
    { name: 'colombia' }
  ]
}];

function filterByKeyValue(arr, keys, val) {
  return arr.filter(item => {
    return item[keys[0]].some(subitem => subitem[keys[1]].indexOf(val) > -1);
  }).map(item => {
    item[keys[0]] = item[keys[0]].filter(subitem => subitem[keys[1]].indexOf(val) > -1);
    return item;
  });
}

var filtered = filterByKeyValue(continents, [ 'countries', 'name' ], 'col');

console.log(filtered);
.as-console-wrapper { top: 0; max-height: 100% !important; }
<!--

Original filter that method is based on.

var filtered = continents.filter(continent => {
  return continent.countries.some(country => country.name.indexOf('col') > -1);
}).map(continent => {
  continent.countries = continent.countries.filter(country => country.name.indexOf('col') > -1);
  return continent;
});

-->

Upvotes: 1

David Anthony Acosta
David Anthony Acosta

Reputation: 4918

item in this case has two properties, countries and short. You are trying to run .name on item, which doesn't exist. You need to run .name on either item.countries[0].name or item.countries[1].name.

    var continents = [
        {
            short: 'na',
            countries: [
                {
                    name: 'canada'
                },
                {
                    name: 'usa'
                }
            ]

        },
        {
            short: 'sa',
            countries: [
                {
                    name: 'chile'
                },
                {
                    name: 'colombia'
                }
            ]

        }
    ];

    var results = continents.filter(item => {
        return item.countries[0].name.toLowerCase().indexOf('col'.toLowerCase()) >= 0 || 
item.countries[1].name.toLowerCase().indexOf('col'.toLowerCase()) >= 0
    });

    console.log(results);

Working example: https://jsfiddle.net/gugui3z24/fnw5kf0x/

Upvotes: 0

Related Questions