v3nt
v3nt

Reputation: 2912

search array of objects.shortName and also nested array of objects

I'm trying to filter a large array of objects that also have nested values.

I need to match shortName OR description OR isoCode. Some of the items may have 20+ countries but most have 1 to 5.

{
 countries: Array(1)
  0:
    description: "United Kingdom"
    isoCode: "GB"
  1:
    description: "Italy"
    isoCode: "IT"
 shortName: "AIB (NI)"
},
// * 2000
 

I've tried building on this with limited success.

  methods: {
    filterInstitutions: function (items: any, event: any): void {
      console.log(items, event.target.value);
      if (event === '') {
        newFunction(items);
      } else {
        this.listedInstitutions = items.filter((item: any) => {
          return item.shortName.toLowerCase().includes(event.target.value.toLowerCase());
        })
      }
    },
  },

I am building this in Vue (typescript) but understand its as much of a JS question than a Vue one.

Any suggestions welcome.

Upvotes: 0

Views: 69

Answers (3)

trixn
trixn

Reputation: 16309

Using filter() and some():

function filterInstitutions (items, {target: {value}}) {
  const isMatch = text => text.toLowerCase().includes(value.toLowerCase());

  return items.filter(({shortName, countries}) => (
    isMatch(shortName) || countries.some(({description, isoCode}) => (
      isMatch(description) || isMatch(isoCode)
    ))
  ));
};

Upvotes: 1

Ohgodwhy
Ohgodwhy

Reputation: 50787

You could create an array of properties to search on within your object(s) and determine if any matches exist.

this.listedInstitutions = items.filter((item: any) => {
  return item.countries.filter(
    country => ['description', 'isoCode', 'shortName'].filter(prop => item.prop && item.prop.toLowerCase().includes(event.target.value.toLowerCase()).length > 0
  ).length > 0
})

Upvotes: 1

Patrick Evans
Patrick Evans

Reputation: 42736

You will need to add in tests for the description and isoCode of your countries property to your filter function.

One way would be to use the Array's some() method which will test if any of the array element's match any test you setup in the callback. It will then return true if any do match and false if not.

let testValue = event.target.value.toLowerCase();
this.listedInstitutions = items.filter((item: any) => {
   //test the shortName
   let matchesShortName = item.shortName.toLowerCase().includes(testValue);

   //loop through countries and see if any description or isoCode match
   let matchesDescIsoCode = item.countries.some((item:any) => {
     let desc = item.description.toLowerCase();
     let code = item.isoCode.toLowerCase();
     return desc.includes(testValue) || code.includes(testValue);
   });
   return matchesShortName || matchesDescIsoCode;
})

Example

function doFilter(event, items) {
  let testValue = event.target.value.toLowerCase();
  let listedInstitutions = items.filter((item) => {
    let matchesShortName = item.shortName.toLowerCase().includes(testValue);
    let matchesDescIsoCode = item.countries.some((item) => {
      let desc = item.description.toLowerCase();
      let code = item.isoCode.toLowerCase();
      return desc.includes(testValue) || code.includes(testValue);
    });
    return matchesShortName || matchesDescIsoCode;
  });
  console.clear();
  console.log("Filtered List");
  console.log(listedInstitutions);
}

let someItems = [{
  countries: [{
      description: "United Kingdom",
      isoCode: "GB"
    },
    {
      description: "Italy",
      isoCode: "IT"
    }
  ],
  shortName: "AIB (NI)"
}, {
  countries: [{
      description: "United States",
      isoCode: "US"
    },
    {
      description: "Italy",
      isoCode: "IT"
    }
  ],
  shortName: "ABC (DE)"
}]
<input type="text" oninput="doFilter(event,someItems)">

Upvotes: 2

Related Questions