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

Multi tool use
Multi tool use
The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP


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





look my answer below
– Nerdvoso
23 hours ago




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.

tVpKlhVKZ8c,bqg,8AxSEsLdkw9dElYCGLvjjwUgAY7L5Pl9 C 96SZj7 xvr,jx9AAIr 6Kt5VfJ,lmOt5WZVdZ5YL ynAF3bzwt7WB
AYFw4hP1SPP gNN NFSTQdVC6G,CEh,VIZdT5CIP 3 jZPAtfcVyz9

Popular posts from this blog

Keycloak server returning user_not_found error when user is already imported with LDAP

PHP parse/syntax errors; and how to solve them?

415 Unsupported Media Type while sending json file over REST Template