0

I have an array of the form

var cars = [
   {name: "BMW X5", topsales: ["USA", "China", "Russia"], maxspeed: 250, users: ["teenage", "ladies", "mens"]}
   {name: "Volkswagen Touareg", topsales: ["USA", "Germany"], maxspeed: 240, users: ["teenage", "mens", "old mens"]}
   etc....
]

I am trying to filter, let's say like this:

var query = {
   topsales: ["USA", "China"],
   users: "teenage"
}
function nestedFilter(targetArray, filters) {
     var filterKeys = Object.keys(filters);
     return targetArray.filter(function (eachObj) {
         return filterKeys.every(function (eachKey) {
             if (!filters[eachKey].length) {
                 return true; 
             }
             return filters[eachKey].includes(eachObj[eachKey]);
          });
     });
};
goodresult = nestedFilter(cars, query);

But the function doesn't work as it should. If the object has one value in the property, then it filters, but if there are several of them, and I need at least one of them to satisfy the search, then it does not filter. Help who can please

  • Not entirely clear what your specific filtering rules are. Any match in either property... any match in both properties... or?? – charlietfl Dec 12 '21 at 20:38
  • The problem is that there are several valid values in the "users" object property, this is an array. One of its values satisfies the filtering condition, but is not filtered, since it is an array. If the property of the "users" object contains the string value "teenager", then the filtering works. But I have an array of values and the problem is that. – Miloshevich Dec 12 '21 at 21:10

3 Answers3

0

I am assuming that you intend to implement an OR functionality because you said at least one of them. Therefore the working code is below.

But before going on reading, please beware of below remarks:

  • I used some instead of every, because some works as or and every works as and. It means that that line will return true if the current car item matches at least one of the filters.

  • You should use item.includes(filter) instead of filter.includes(item).

  • You need to check if the current filter item is an array or not, and act accordingly.

  • In this code I didn't handle that and assumed that currentCandidate is a string or a primitive. If there are other cases where the candidate item (i.e. a field of the car) itself is also an array, then you have to update the code to also handle that.


  var cars = [
    {name: "BMW X5", topsales: "USA, China, Russia", maxspeed: 250, users: "teenage, ladies, men"},
    {name: "Volkswagen Touareg", topsales: "USA, Germany", maxspeed: 240, users: "teenage, men, old men"}
  ]
  
  var query = {
    topsales: ["USA", "China"],
    maxspeed: 240
  }
  
  function nestedFilter(targetArray, filters) {
    const filterKeys = Object.keys(filters);
    return targetArray.filter(function (eachObj) {
      //using some instead of every to make sure that it works as OR
      const result = filterKeys.some(function (eachKey) {
        //the current item that we are trying to use in the filter
        const currentCandidate = eachObj[eachKey];
  
        //the current item that we are using as a filter
        const currentFilterItem = filters[eachKey]
  
        if (Array.isArray(currentFilterItem)) {
          if (currentFilterItem.length === 0) {
            //no filter, return true
            return true
          }
  
          //loop on each item in the currentFilterItem
          //if any of them matches simply return true (OR)
          for (let filterKey in currentFilterItem) {
            if (currentCandidate.includes(currentFilterItem[filterKey])) {
              return true
            }
          }
          //for loop ended, no match
          return false
        } else {
          //the current filter item is not an array, use it as one item
          //return eachObj[eachKey].includes(currentFilterItem)
          return currentCandidate === currentFilterItem
        }
      });
  
      return result;
    });
  }
  
  goodresult = nestedFilter(cars, query);
  console.debug(goodresult)
yerlilbilgin
  • 3,041
  • 2
  • 26
  • 21
0

You could check if query is an array and/or the value is an array and check accordingly.

function nestedFilter(data, query) {
    const
        filters = Object.entries(query);

    return data.filter(o => filters.every(([k, v]) => Array.isArray(v)
        ? Array.isArray(o[k])
            ? v.some(s => o[k].includes(s))
            : v.includes(o[k])
        : Array.isArray(o[k])
            ? o[k].includes(v)
            : o[k] === v
    ));
}

const
    cars = [{ name: "BMW X5", topsales: ["USA", "China", "Russia"], maxspeed: 250, users: ["teenage", "ladies", "mens"] }, { name: "Volkswagen Touareg", topsales: ["USA", "Germany"], maxspeed: 240, users: ["teenage", "mens", "old mens"] }],
    query = { topsales: ["USA", "China"], users: "teenage" };

console.log(nestedFilter(cars, query));
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
0

You can check if the value of the "filterKey" is not an array, make it an array, and check if an array has a subArray

function hasSubArray(master, sub) {
  return sub.every((i => v => i = master.indexOf(v, i) + 1)(0));
}

function nestedFilter(targetArray, filters) {
  var filterKeys = Object.keys(filters);
  return targetArray.filter(function (eachObj) {
    return filterKeys.every(function (eachKey) {
      var subArray = filters[eachKey];
      if (!Array.isArray(filters[eachKey])) {
        subArray = [filters[eachKey]];
      }

      return hasSubArray(eachObj[eachKey], subArray);
    });
  });
}
masoud zarean
  • 33
  • 1
  • 6