How do I filter nested array of objects on multiple conditions?

Multi tool use


How do I filter nested array of objects on multiple conditions?
Below is the sample array of objects.
I'm looking to filter this on basis of criteriaType
, id
& source
. If none of the input.source
match, the parent object should be filtered out. Also all the filter criteria are optional.
criteriaType
id
source
input.source
[{
"id": "9be6c6299cca48f597fe71bc99c37b2f",
"caption": "caption1",
"criteriaType": "type2",
"input": [
{
"id_1": "66be4486ffd3431eb60e6ea6326158fe",
"criteriaId": "9be6c6299cca48f597fe71bc99c37b2f",
"source": "type1",
},
{
"id_1": "1ecdf410b3314865be2b52ca9b4c8539",
"criteriaId": "9be6c6299cca48f597fe71bc99c37b2f",
"source": "type2",
}
]
},
{
"id": "b83b3f081a7b45e087183740b12faf3a",
"caption": "caption1",
"criteriaType": "type1",
"input": [
{
"id_1": "f46da7ffa859425e922bdbb701cfcf88",
"criteriaId": "b83b3f081a7b45e087183740b12faf3a",
"source": "type3",
},
{
"id_1": "abb87219db254d108a1e0f774f88dfb6",
"criteriaId": "b83b3f081a7b45e087183740b12faf3a",
"source": "type1",
}
]
},
{
"id": "fe5b071a2d8a4a9da61bbd81b9271e31",
"caption": "caption1",
"criteriaType": "type1",
"input": [
{
"id_1": "7ea1b85e4dbc44e8b37d1110b565a081",
"criteriaId": "fe5b071a2d8a4a9da61bbd81b9271e31",
"source": "type3",
},
{
"id_1": "c5f943b61f674265b8237bb560cbed03",
"criteriaId": "fe5b071a2d8a4a9da61bbd81b9271e31",
"source": "type3",
}
]
}]
I was able to achieve just filter by criteriaType
& id
. But I'm not able to filter by source
also to make sure that parent isn't returned if none of the input.source match.
criteriaType
id
source
var json = <<array of objects>> ;
const {objectId: id, ctype: criteriaType, inputSource: source } = param; // getting the the params
json = ctype ? json.filter(({criteriaType}) => criteriaType === ctype ): json;
json = (objectId ? json.filter(({id}) => id === objectId ): json)
.map (({id, caption, criteriaType, input }) => {
//some manipulation
return { //results after manipulation}
})
Help me out! Thanks in advance. I'm not sure if we could chain filters to achieve it.
looking for esLint compatible code
6 Answers
6
There are a couple approaches to this. You can implement this in pure JS, and I recommend Lodash:
1) Lodash filters
``` javascript
var users = [
{ 'user': 'barney', 'age': 36, 'active': true },
{ 'user': 'fred', 'age': 40, 'active': false }
];
_.filter(users, function(o) { return !o.active; });
// => objects for ['fred']
// The `_.matches` iteratee shorthand.
_.filter(users, { 'age': 36, 'active': true });
// => objects for ['barney']
// The `_.matchesProperty` iteratee shorthand.
_.filter(users, ['active', false]);
// => objects for ['fred']
// The `_.property` iteratee shorthand.
_.filter(users, 'active');
// => objects for ['barney']
```
2) JavaScript ES5 filter()
``` javascript
var words = ['spray', 'limit', 'elite', 'exuberant', 'destruction', 'present'];
var result = words
.filter(word => word.length > 6)
.filter(word => word.length < 8);
console.log(result);
// expected output: Array ["present"]
```
2) MapReduce
MapReduce is one of my favorite tools for working with sets/collections.
You used map()
in your code above. The trick might be to change map
to reduce
.
map()
map
reduce
With map
, you get a 1-1 ratio of collection items in and out.
map
With reduce
, you get as many items as you like generated for each item in the input. E.g.
reduce
``` javascript
var stuff = ['couch', 'chair', 'desk'];
var hasFiveLetters = stuff.reduce((total, item) => {
if (item.length === 5) total.push(item); // add to total any items you like
return total; // don't forget to return total!
}, ); // initialize total to
console.log(hasFiveLetters); // ['couch', 'chair'];
```
I can't use lodash, I did lookup multiple filters chaining - but didn't succeed. also can't use lodash :(
– thegeekajay
yesterday
@thegeekajay You might try .reduce() in place of your .map() above...
– Michael Cole
yesterday
requirements are filters are optional, and none of the source matches parent shall not be returned https://jsfiddle.net/cpk18dt4/9/
Comments are in the code. hope it explains what the function does.
const fnFilter = (criteriaType, id, source) => {
let result = oData;
if (criteriaType) { // it can be null (optional)
result = result.filter(d => d.criteriaType === criteriaType);
}
if (id) { // it can be null (optional)
result = result.filter(d => d.id === id);
}
if (source) { // it can be null (optional)
result = result.filter(d => {
const inputs = d.input.filter(inp => inp.source === source);
// If none of the input.source match, the parent object should be filtered out
if (inputs.length === 0) {
return false;
}
d.input = inputs;
return true;
});
}
return result;
};
From this solution, I understood the actual problem.
– Er_sherlockian
yesterday
Thanks a lot :) Lemme work more on it and see if it could be optimized more.
– thegeekajay
yesterday
Try this:
var obj = [{
"id": "9be6c6299cca48f597fe71bc99c37b2f",
"caption": "caption1",
"criteriaType": "type2",
"input": [
{
"id_1": "66be4486ffd3431eb60e6ea6326158fe",
"criteriaId": "9be6c6299cca48f597fe71bc99c37b2f",
"source": "type1",
},
{
"id_1": "1ecdf410b3314865be2b52ca9b4c8539",
"criteriaId": "9be6c6299cca48f597fe71bc99c37b2f",
"source": "type2",
}
]
},
{
"id": "b83b3f081a7b45e087183740b12faf3a",
"caption": "caption1",
"criteriaType": "type1",
"input": [
{
"id_1": "f46da7ffa859425e922bdbb701cfcf88",
"criteriaId": "b83b3f081a7b45e087183740b12faf3a",
"source": "type3",
},
{
"id_1": "abb87219db254d108a1e0f774f88dfb6",
"criteriaId": "b83b3f081a7b45e087183740b12faf3a",
"source": "type1",
}
]
},
{
"id": "fe5b071a2d8a4a9da61bbd81b9271e31",
"caption": "caption1",
"criteriaType": "type1",
"input": [
{
"id_1": "7ea1b85e4dbc44e8b37d1110b565a081",
"criteriaId": "fe5b071a2d8a4a9da61bbd81b9271e31",
"source": "type3",
},
{
"id_1": "c5f943b61f674265b8237bb560cbed03",
"criteriaId": "fe5b071a2d8a4a9da61bbd81b9271e31",
"source": "type3",
}
]
}];
function filterObj(obj, column, value){
var newArray = obj.filter(function (el) {
if(el[column]){
return el[column] == value;
}else{
for(var key in el.input){
if(typeof(el.input[key] == "object")){
var item = el.input[key];
if(item[column] == value){return item;}
}
}
}
});
return newArray;
}
console.log(filterObj(obj, 'caption','caption1'));
console.log(filterObj(obj, 'criteriaId','fe5b071a2d8a4a9da61bbd81b9271e31'));
console.log(filterObj(obj, 'id_1','1ecdf410b3314865be2b52ca9b4c8539'));
This is an alternative using the function filter
along with the function some
.
filter
some
This approach is only to show how to filter the array according to inputSource
and attribute source
inputSource
source
let arr = [{ "id": "9be6c6299cca48f597fe71bc99c37b2f", "caption": "caption1", "criteriaType": "type2", "input": [ { "id_1": "66be4486ffd3431eb60e6ea6326158fe", "criteriaId": "9be6c6299cca48f597fe71bc99c37b2f", "source": "type1", }, { "id_1": "1ecdf410b3314865be2b52ca9b4c8539", "criteriaId": "9be6c6299cca48f597fe71bc99c37b2f", "source": "type2", } ]},{ "id": "b83b3f081a7b45e087183740b12faf3a", "caption": "caption1", "criteriaType": "type1", "input": [ { "id_1": "f46da7ffa859425e922bdbb701cfcf88", "criteriaId": "b83b3f081a7b45e087183740b12faf3a", "source": "type3", }, { "id_1": "abb87219db254d108a1e0f774f88dfb6", "criteriaId": "b83b3f081a7b45e087183740b12faf3a", "source": "type1", } ]},{ "id": "fe5b071a2d8a4a9da61bbd81b9271e31", "caption": "caption1", "criteriaType": "type1", "input": [ { "id_1": "7ea1b85e4dbc44e8b37d1110b565a081", "criteriaId": "fe5b071a2d8a4a9da61bbd81b9271e31", "source": "type3", }, { "id_1": "c5f943b61f674265b8237bb560cbed03", "criteriaId": "fe5b071a2d8a4a9da61bbd81b9271e31", "source": "type3", } ]}],
inputSource = 'type2',
result = arr.filter(o => o.input.some(i => i.source === inputSource));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
If you need to filter the nested array according to inputSource
inputSource
let arr = [{ "id": "9be6c6299cca48f597fe71bc99c37b2f", "caption": "caption1", "criteriaType": "type2", "input": [ { "id_1": "66be4486ffd3431eb60e6ea6326158fe", "criteriaId": "9be6c6299cca48f597fe71bc99c37b2f", "source": "type1", }, { "id_1": "1ecdf410b3314865be2b52ca9b4c8539", "criteriaId": "9be6c6299cca48f597fe71bc99c37b2f", "source": "type2", } ]},{ "id": "b83b3f081a7b45e087183740b12faf3a", "caption": "caption1", "criteriaType": "type1", "input": [ { "id_1": "f46da7ffa859425e922bdbb701cfcf88", "criteriaId": "b83b3f081a7b45e087183740b12faf3a", "source": "type3", }, { "id_1": "abb87219db254d108a1e0f774f88dfb6", "criteriaId": "b83b3f081a7b45e087183740b12faf3a", "source": "type1", } ]},{ "id": "fe5b071a2d8a4a9da61bbd81b9271e31", "caption": "caption1", "criteriaType": "type1", "input": [ { "id_1": "7ea1b85e4dbc44e8b37d1110b565a081", "criteriaId": "fe5b071a2d8a4a9da61bbd81b9271e31", "source": "type3", }, { "id_1": "c5f943b61f674265b8237bb560cbed03", "criteriaId": "fe5b071a2d8a4a9da61bbd81b9271e31", "source": "type3", } ]}],
inputSource = 'type2';
arr.forEach(o => o.input = o.input.filter(io => io.source === inputSource));
console.log(arr);
.as-console-wrapper { max-height: 100% !important; top: 0; }
this still doesn't answer my question. I'm able to filter by ctype and id. But how do i do on input.source ?
– thegeekajay
yesterday
@thegeekajay can you share the desired output?
– Ele
yesterday
Lodash find and filter can be used for this. Find will be faster in nested array as compared to Filter it needs to match first occurrence.
json = ctype ? _.filter(json, function(o) {
return o.criteriaType === ctype;
}) || json;
json = objectId ? _.filter(json, function(o) {
return o.id === objectId;
}) || json;
json = source ? _.filter(json, function(o) {
return _.find(o.input, function(input_object) {
return input_object.source === source;
});
}) || json;
// if field has a value, filter on it, else return original json
const having = (json, field, val) =>
val ? json.filter(j => j[field] === val) : json
const filterBySource = (json, source) => {
if (!source) return json
return json.filter(
j => j.input.length > 0 && j.input.some(input => input.source === source)
)
}
function search(json, params = {}) {
const { objectId: id, ctype: criteriaType, inputSource: source } = params
// if no filters, return the whole json
if (!(id || criteriaType || source)) return json
let result
result = having(json, 'id', id)
result = having(result, 'criteriaType', criteriaType)
return filterBySource(result, source)
}
const params = {
objectId: 'fe5b071a2d8a4a9da61bbd81b9271e31',
ctype: 'type1',
inputSource: 'type3'
}
search(json, params).map(({ id, caption, criteriaType, input }) => {
// do something with filtered json
})
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
look my answer below
– Nerdvoso
23 hours ago