51

I am trying to filter an array, based on some nested object. I prepared some Fiddle

Input array looks like this:

let arrayOfElements = 
    [
        {
           "name": "a",
           "subElements": 
           [
             {"surname": 1},
             {"surname": 2}
           ]
        },
        {
           "name": "b",
           "subElements": 
           [
             {"surname": 3},
             {"surname": 1}
           ]
        },
        {
           "name": "c",
           "subElements": 
           [
             {"surname": 2},
             {"surname": 5}
           ]
        }
    ];

I want the output for this case, to look like this:

let filteredArray = 
    [
        {
          "name": "a",
          "subElements": 
          [
            {"surname": 1}
          ]
        },
        {
          "name": "b",
          "subElements": 
          [
            {"surname": 1}
          ]
        }
];

I am using this formula to do that:

let filteredArray = arrayOfElements.filter((element) => element.subElements.some((subElement) => subElement.surname === 1));

Output is almost good, but it returns objects with all objects with surnames (better check that fiddle :D), instead of cutting them away. How can i improve the filtering ?

1
  • Are you trying to group based on surname? I mean same thing should group a and c since they have surname 2. Right? Commented Jul 14, 2016 at 13:34

10 Answers 10

82

This way you can go as deep as you want in an array and filter elements at any level,

arrayOfElements.map((element) => {
  return {...element, subElements: element.subElements.filter((subElement) => subElement.surname === 1)}
})

Spread operator will expand element and then filtered subElements will override the subElements in element.

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

10 Comments

this should be the answer because the accepted answer is not producing the expected output
Yes, this answer is better. When the accepted answer was written, the Object spread operator did not exist.
@Aadam can you share your array ? Array.map doesn't modify the original array but returns a new array with modified values.
This solution has the problem that is will return the same number of elements as the original. They will have the subElements array empty, so a correction would be to add a filter to the result map by checking the length of subElements.
This is not removing the third object completely
|
30

After you call filter, you need to pipe the results to map, like this:

let filteredArray = arrayOfElements
  .filter((element) => 
    element.subElements.some((subElement) => subElement.surname === 1))
  .map(element => {
    let newElt = Object.assign({}, element); // copies element
    return newElt.subElements.filter(subElement => subElement.surname === '1');
  });

I am assuming here that you don't want to manipulate the original array. So, I am using Object.assign.

6 Comments

isn't this returning just the array of subElements instead of the full object with the filtered subarray?
@WannyMiarelli is correct. It will return the filtered subarrays and not the full object with the filtered surnames. Also the code above has errors (string check instead of integer and typo surName).
this solution is not producing expected output : jsbin.com/dunuqeyeje/edit?js,console
@LokeshSanapalli, in your final call to filter you need to change surName to surname. Then it works. That was my mistake above. Corrected now.
Hi @AndrewEisenberg! Thanks for your input. Now after 4 years I've decided to change the accepted answer to one that is using spread operator since it seems to be better suited for 2020 :) Thanks, and no hard feelings, I hope :)
|
25
let filteredArray = arrayOfElements
  .filter((element) => 
    element.subElements.some((subElement) => subElement.surname == 1))
  .map(element => {
    return Object.assign({}, element, {subElements : element.subElements.filter(subElement => subElement.surname == 1)});

  }); 

3 Comments

this will give the expected response when you'll merge the filtered array with parent object "subElement".
this returned the expected response
This worked for me, same context and I got the exact response
9

Just improved the answers above

let elements = 
    [
        {
           "name": "a",
           "subElements": 
           [
             {"surname": 1},
             {"surname": 2}
           ]
        },
        {
           "name": "b",
           "subElements": 
           [
             {"surname": 3},
             {"surname": 1}
           ]
        },
        {
           "name": "c",
           "subElements": 
           [
             {"surname": 2},
             {"surname": 5}
           ]
        }
    ];
var value = 1;

var filteredArray = elements
.filter(element => element.subElements
  .some(subElement => subElement.surname === value)
)
.map(element => {
  let n = Object.assign({}, element, {'subElements': element.subElements.filter(
    subElement => subElement.surname === value
  )})
  return n;
})

console.log(filteredArray)

Comments

7

Try this solution:

data_filter = arrayOfElements.filter(function (element) {
    return element.subElements.some( function (subElement) {
        return subElement.surname === surname
    });
});

1 Comment

it will not return exact expected response, but return all the surnames within an object found
1

You can make it generic as well:

Logic

  • Find all distinct surnames and loop over them
  • Filter every object to check if surnames exists. If yes, copy object using Object.assign and set subElements value to filtered list.
  • Create a temp array to hold all similar objects and push copied object to it.
  • Push this array to final array on every iteration of distinct surname.

Sample

let arrayOfElements=[{name:"a",subElements:[{surname:1},{surname:2}]},{name:"b",subElements:[{surname:3},{surname:1}]},{name:"c",subElements:[{surname:2},{surname:5}]}];
 let distinct_surnames = [];
 arrayOfElements.forEach(function(el) {
   el.subElements.forEach(function(s) {
     if (distinct_surnames.indexOf(s.surname) < 0) distinct_surnames.push(s.surname)
   });
 })

 let result = [];
 distinct_surnames.forEach(function(sn) {
   let inter = [];
   arrayOfElements.forEach(function(el) {
     let f = el.subElements.filter(function(sub) {
       return sub.surname === sn;
     });
     if (f.length > 0) {
       let _tmp = Object.assign({}, el);
       _tmp.subElements = f;
       inter.push(_tmp);
     }
   });
   result.push(inter);
 })
 console.log(result)

Note: Arrow functions are used to keep the reference of this. If you are not using this inside function, you can use normal functions as well.

Comments

0

function display_message() {
  let arrayOfElements = [{
    "name": "a",
    "subElements": [{
      "surname": 1
    }, {
      "surname": 2
    }]
  }, {
    "name": "b",
    "subElements": [{
      "surname": 3
    }, {
      "surname": 1
    }]
  }, {
    "name": "c",
    "subElements": [{
      "surname": 2
    }, {
      "surname": 5
    }]
  }];
  // console.log(arrayOfElements);
var surname = 1;
  let filteredArray = arrayOfElements.filter((element) => element.subElements.some((subElement) => subElement.surname === surname));

  for(var data in filteredArray){
    filteredArray[data].subElements = {"surname": surname};
    }
  console.log(filteredArray);

}
<input type="button" onclick="display_message();" value="click"/>

Comments

0
let filteredArray = arrayOfElements
    .filter((element) => 
        element.subElements.some((subElement) => subElement.surname === 1))
    .map(element => {
        let newElt = Object.assign({}, element); // copies element
        newElt.subElements = newElt.subElements.filter(subElement => subElement.surName === '1'); 
        return newElt;
    });

is more correctly

Comments

0

Only because I'm lazy

const arrayOfElements = [
  {
    name: 'a',
    subElements: [{ surname: 1 }, { surname: 2 }],
  },
  {
    name: 'b',
    subElements: [{ surname: 3 }, { surname: 1 }],
  },
  {
    name: 'c',
    subElements: [{ surname: 2 }, { surname: 5 }],
  },
]

const filteredArr = arrayOfElements
  .filter(({ subElements }) => subElements.some(({ surname }) => surname === 1))
  .map(({ subElements, ...rest }) => ({ subElements: subElements.filter(({ surname }) => surname === 1), ...rest }))

console.log(filteredArr)

1 Comment

Your answer needs more details and clarity
-1

let dataSource = [
  {
    groupName: 'FORD',
    optionItem: [
      {
        label: 'Fiesta',
        data: 'Fiesta',
      },
      {
        label: 'Fusion',
        data: 'Fusion',
      },
      {
        label: 'Mustang',
        data: 'Mustang',
      },
    ],
  },
  {
    groupName: 'HONDA',
    optionItem: [
      {
        label: 'Accord',
        data: 'Accord',
      },
      {
        label: 'Civic',
        data: 'Civic',
      },
      {
        label: 'CR-V',
        data: 'CR-V',
      },
    ],
  },
  {
    groupName: 'HYUNDAI',
    optionItem: [
      {
        label: 'Accent',
        data: 'Accent',
      },
      {
        label: 'Sonata',
        data: 'Sonata',
      },
      {
        label: 'Tucson',
        data: 'Tucson',
      },
    ],
  },
];

let filterValue = dataSource.filter(a => {
  a.optionItem = a.optionItem.filter(
    option => option.data.toLowerCase().indexOf('Fiesta'.toLowerCase()) !== -1
  );
  return a.optionItem.length;
});

console.log('filter value ', filterValue);


/**
OUTPUT
=========================================
filter value =>  [{
  "groupName": "FORD",
  "optionItem": [
    {
      "label": "Fiesta",
      "data": "Fiesta"
    }
  ]
}] 
**/

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.