2

I want to filter objects based on a set of arrays containing filter terms. It basically works until one of the filter arrays contains multiple terms. Here is the code:

// the filters
const filters = {
	"eyeColor": ["blue"],
	"gender": ["male"],
	"age": ["33"],
	"tags": ["d3js", "vuejs"] // multiple terms in the filter array breaks the code (returns empty array)
}

// the data 
const users = [
	{
		"age": "33",
		"eyeColor": "blue",
		"tags": "d3js, max, grotesk",
		"gender": "male",
	},
	{
		"age": "31",
		"eyeColor": "blue",
		"tags": "vuejs, adrian,  serif",
		"gender": "male",
	},
	{
		"age": "37",
		"eyeColor": "brown",
		"tags": "vuejs, max,  mono, d3js",
		"gender": "female",
	},
	{
		"age": "33",
		"eyeColor": "blue",
		"tags": "vuejs, markus, grotesk",
		"gender": "male",
	},
]

// the filter function
let results = users.filter(function (object) {
	return Object.entries(filters).every(function ([key, value]) {		
		return value.every(function (filter) {
			return object[key].includes(filter)
		})
	})
});
console.log(results);

I get an empty array, while the expected result would be:

{
   "age": "33",
   "eyeColor": "blue",
   "tags": "d3js, max, grotesk",
   "gender": "male",
},
{
    "age": "33",
    "eyeColor": "blue",
    "tags": "vuejs, markus, grotesk",
    "gender": "male",
}

How can I get the expected result?

4
  • 1
    Can you change the format of users so that tags will also be an array of "tags" instead of a string? Commented Dec 18, 2019 at 17:08
  • I guess theoretically this would be possible. However, it's working now! Thank you Commented Dec 19, 2019 at 11:42
  • 1
    I just asked because with this approach the tag serif would also match sans-serif Commented Dec 19, 2019 at 11:54
  • Good thought! Thank you, I will keep that in mind while pursuing the project Commented Dec 19, 2019 at 14:14

1 Answer 1

2

The code uses

return value.every(function (filter) ...
//           ^^^^^

but you don't want to match all of the tags in the permitted array, only some (any):

return value.some(function (filter) ...
//           ^^^^

Here's a demonstration:

const filters = { "eyeColor": ["blue"], "gender": ["male"], "age": ["33"], "tags": ["d3js", "vuejs"] }
const users = [{ "age": "33", "eyeColor": "blue", "tags": "d3js, max, grotesk", "gender": "male", }, { "age": "31", "eyeColor": "blue", "tags": "vuejs, adrian,  serif", "gender": "male", }, { "age": "37", "eyeColor": "brown", "tags": "vuejs, max,  mono, d3js", "gender": "female", }, { "age": "33", "eyeColor": "blue", "tags": "vuejs, markus, grotesk", "gender": "male", }, ];

const filterEntries = Object.entries(filters);
const results = users.filter(user =>
  filterEntries.every(([key, permitted]) =>
    permitted.some(e => user[key].includes(e))
  )
);
console.log(results);

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

4 Comments

Hope you don't mind the edit - started writing an answer but saw no real need to finish once you posted yours, so I just added the runnable snippet.
I noticed that by filtering for "gender": ["male"], it returns also female because it contains "male". In some cases this is great, but would there be a solution to avoid this or is this locically inconsequent?
Right--the problem is that we're using includes which checks substrings. As mentioned above in the comments, it's poor design to have a comma-delimited list--better to use an array or Set of strings and then use has or includes. You can work around this using your current design with a regex that enforces word boundaries, like e => new RegExp(\\b${e}\\b).test(user[key]), or split the string on commas and call includes on that, but that's going to be a performance hit since it's done in a nested loop. Better to pre-process.
Thank you for the clarification! In this case, I will try to follow the advice from above and design the data structure in a way that I have a list of strings rather than checking for substrings. Performance is a key aspect of this development.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.