8

I'm trying to make a filter. The number of filters will change dynamically, a number of keys can be different, and the number of values, too.

This is how data look like:

var data = [
{id: "123", color: "Red", model: "Tesla"}, 
{id: "124", color: "Black", model: "Honda"}, 
{id: "125", color: "Red", model: "Audi"}, 
{id: "126", color: "Blue", model: "Tesla"}]

Filter keys are color and model. But sometimes I will filter only by color or model and sometimes by both. I want to make a function that will cover both cases. Also, a user can choose many values (Tesla, Honda...).

Key can be only color, or only model, or both. Values can look like: only "Red", "Red" and "Blue", or "Red" and "Tesla", or "Red", "Blue" and "Tesla"... Depends on what user choose.

I tried this:

var filtered = [];
data.forEach(item => {
   filterByKey.forEach(key => {
      values.forEach(value => {
         if (item[key] === value) {
             filtered.push(item);
          }
       });
    });
  });

Here is JsFiddle

My loop works well when I have one filter key, but it doesn't work well when I have more than one key. Is it a good idea to pass keys and values as an array?

No jQuery please, only pure JavaScript.

8 Answers 8

12

You can use filter() with every() and check if value of current object with current key exits in values array using includes()

var data = [{"id":"123","color":"Red","model":"Tesla"},{"id":"124","color":"Black","model":"Honda"},{"id":"125","color":"Red","model":"Audi"},{"id":"126","color":"Blue","model":"Tesla"}]

var keys = ["color", 'model'];
var values = ["Tesla", "Audi", "Red"];

var result = data.filter(function(e) {
  return keys.every(function(a) {
    return values.includes(e[a])
  })
})

console.log(result);

Sign up to request clarification or add additional context in comments.

4 Comments

This is exactly what I was looking for! Thanks!
Hey Nenad, function works great, but working with it I figured out one case we missed. That case is when same value repeats for values. Color and model have same value. It's a stupid example, but let's say a color is "Red" and model is "Red", too. Should I maybe pass key and values together as an object? What do you suggest? Thanks!
Hey, Nenad? Can you help me, please?
@snoopy25 I don't see the problem jsfiddle.net/Lg0wyt9u/2114 it will return it.
6

You could use a combined approach with a seach object which keeps the conditions, like

{ 
    model: 'Tesla',                                    // a single value
    color: ['red', 'blue'],                            // a some value
    price: {                                           // a range/interval
        min: 2000, 
        max: 3000
    },
    transmission: v => v.toLowerCase() === 'automatic' // a function
}

var useConditions = search => a => Object.keys(search).every(k => 
        a[k] === search[k] ||
        Array.isArray(search[k]) && search[k].includes(a[k]) ||
        typeof search[k] === 'object' && +search[k].min <= a[k] &&  a[k] <= +search[k].max ||
        typeof search[k] === 'function' && search[k](a[k])
    ),
    data = [{ id: "123", color: "Red", model: "Tesla" }, { id: "124", color: "Black", model: "Honda" }, { id: "125", color: "Red", model: "Audi" }, { id: "126", color: "Blue", model: "Tesla" }],
    filters = { color: ['Red', 'Blue'], model: 'Tesla' };

console.log(data.filter(useConditions(filters)));

1 Comment

this should be the accepted answer, simple and amazing ! thanks
6

You can use Array.prototype.filter() where the function to test each element of the array is:

el => !!filterBy.toString().match(new RegExp(`(?=.*${el.color})(?=.*${el.model})`))

It consist of a regular expression new RegExp(`(?=.*${el.color})(?=.*${el.model})`)) that match to strings color and model in another string filterBy.toString()

var data = [{id: "123", color: "Red", model: "Tesla"}, {id: "124", color: "Black", model: "Honda"}, {id: "125", color: "Red", model: "Audi"}, {id: "126", color: "Blue", model: "Tesla"}],
    filterBy = ['Tesla', 'Audi', 'Red', 'Black'],
    result = data.filter(el => !!filterBy.toString().match(new RegExp(`(?=.*${el.color})(?=.*${el.model})`)));

console.log(result);

And also, you can combine Array.prototype.filter() and Array.prototype.includes():

var data = [{id: "123", color: "Red", model: "Tesla"}, {id: "124", color: "Black", model: "Honda"}, {id: "125", color: "Red", model: "Audi"}, {id: "126", color: "Blue", model: "Tesla"}],
    filterBy = ['Tesla', 'Audi', 'Red', 'Black'],
    result = data.filter(el => filterBy.includes(el.model) && filterBy.includes(el.color));

console.log(result);

Comments

1

For a case like this here's what I just do:

function _filter(data, query) { // query = "Red" or "Tesla"
    return data.filter(el => {
        return el.model.toLowerCase().indexOf(query.toLowerCase()) !== -1 ||
        el.name.toLowerCase().indexOf(query.toLowerCase()) !== -1;
    });
}

Now this function searches data on the model key and name key.

Comments

1

enter image description here

Using filter() to filter data, combined with some() to check if multiple keys match at least one of the strings from the values using includes()

// array --------------------

const data = [
  { id: '123', color: 'Red', model: 'Tesla' },
  { id: '124', color: 'Black', model: 'Honda' },
  { id: '125', color: 'Red', model: 'Audi' },
  { id: '126', color: 'Blue', model: 'Tesla' },
];



// filter exact match --------------------

const keysExact = ['color', 'model'];
const valuesExact = ['Tesla', 'Audi', 'Red'];

const resultExact = data.filter((item) =>
  keysExact.every((a) => valuesExact.includes(item[a]))
);

console.log(resultExact);
// result:
// [
//   { id: '123', color: 'Red', model: 'Tesla' },
//   { id: '125', color: 'Red', model: 'Audi' },
// ]; 

enter image description here

Using filter() to filter data, combined with some() to check if multiple keys contain at least one of the strings from the values using includes().

// array --------------------

const data = [
  { id: '123', color: 'Red', model: 'Tesla' },
  { id: '124', color: 'Black', model: 'Honda' },
  { id: '125', color: 'Red', model: 'Audi' },
  { id: '126', color: 'Blue', model: 'Tesla' },
];



// filter data by keys containing values string (at least one of the string in values) --------------------

const keysSome = ['color', 'model'];
const valuesSome = ['Tes', 're'];

const resultSome = data.filter((item) =>
  keysSome.some((key) =>
    valuesSome.some((val) => item[key].toLowerCase().includes(val.toLowerCase()))
  )
);

console.log(resultSome);
// result:
// [
//   { id: '123', color: 'Red', model: 'Tesla' },
//   { id: '125', color: 'Red', model: 'Audi' },
//   { id: '126', color: 'Blue', model: 'Tesla' },
// ];

enter image description here

Using filter() to filter data, combined with every() to check if multiple keys contain all strings from the values using includes().

// array --------------------

const data = [
  { id: '123', color: 'Red', model: 'Tesla' },
  { id: '124', color: 'Black', model: 'Honda' },
  { id: '125', color: 'Red', model: 'Audi' },
  { id: '126', color: 'Blue', model: 'Tesla' },
];



// filter data by keys containing values string (matches all strings from values) --------------------

const keysEvery = ['color', 'model'];
const valuesEvery = ['Tes', 're'];

const resultEvery = data.filter((item) =>
  keysEvery.every((key) =>
    valuesEvery.some((val) => item[key].toLowerCase().includes(val.toLowerCase()))
  )
);

console.log(resultEvery);
// result:
// [
//   { id: '123', color: 'Red', model: 'Tesla' }
// ]

2 Comments

Did you use a specific website to get these visual search results ?
I actually design it in Figma since I'm a designer as well.
0

Is this what you want to achieve?

var data = [
  {id: "123", color: "Red", model: "Tesla"}, 
  {id: "124", color: "Black", model: "Honda"}, 
  {id: "125", color: "Red", model: "Audi"}, 
  {id: "126", color: "Blue", model: "Tesla"}
];

var allowedColors = ["Red", "Blue"];
var allowedModels = ["Tesla", "Audi"];

function filter (cars, colors, models) {
  return cars.filter(function (car) {
    return colors.indexOf(car.color) !== -1 && // check if the car's color is allowed
      models.indexOf(car.model) !== -1;        // check if the car's model is allowed
  });
}

var filtered = filter(data, allowedColors, allowedModels);
console.log(filtered);

2 Comments

I think he might want to be able to set models or colors to null, with the meaning "all".
This works well only for this case, but if I want to group filter only colors or only models this doesn't work.
0

Filtering is native in JS, try this:

data.filter(item => item.color==='Red');
// or in the old fashion 
data.filter(function(item){ return item.color==='Red';} );

Comments

0

Here, I have a different scenario.

I have an object

d={r1: {name: "r1", validation: true}, r2: {name: "r2", validation: true}};

and another array

a = ["r1"];

Here, the result I want is

d={r1: {name: "r1", validation: true}}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.