Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

javascript filter nested object based on key value

I wish to filter a nested javascript object by the value of the "step" key:

var data = {
"name": "Root",
"step": 1,
"id": "0.0",   
"children": [
    {
    "name": "first level child 1",
    "id": "0.1",
    "step":2,
    "children": [
        {
        "name": "second level child 1",
        "id": "0.1.1",
        "step": 3,
        "children": [
            {
            "name": "third level child 1",
            "id": "0.1.1.1",
            "step": 4,
            "children": []},
        {
            "name": "third level child 2",
            "id": "0.1.1.2",
            "step": 5,
            "children": []}

        ]},
                ]}
]

};

var subdata = data.children.filter(function (d) {
        return (d.step <= 2)});

This just returns the unmodified nested object, even if I put value of filter to 1. does .filter work on nested objects or do I need to roll my own function here, advise and correct code appreciated. cjm

like image 734
murray3 Avatar asked Jun 27 '26 18:06

murray3


2 Answers

Recursive filter functions are fairly easy to create. This is an example, which strips a JS object of all items defined ["depth","x","x0","y","y0","parent","size"]:

function filter(data) {
  for(var i in data){
    if(["depth","x","x0","y","y0","parent","size"].indexOf(i) != -1){
       delete data[i]; 
    } else if (i === "children") {
      for (var j in data.children) {
        data.children[j] = filter(data.children[j])
      }
    }  
  }
  return data;
}

If you would like to filter by something else, just updated the 2nd line with your filter function of choice.

like image 170
Paul Avatar answered Jun 30 '26 08:06

Paul


Here's the function to filter nested arrays:

const filter = arr => condition => {
    const res = [];
    for (const item of arr) {
        if (condition(item)) {
            if (!item.children) {
                res.push({ ...item });
            } else {
                const children = filter(item.children)(condition);
                res.push({ ...item, children })
            }
        }
    }
    return res;
}

The only thing you have to do is to wrap your root object into an array to reach self-similarity. In common, your input array should look like this:

data = [
    { <...>, children: [
        { <...>, children: [...] },
        ...
    ] },
    ...
]

where <...> stands for some properties (in your case those are "name", "step" and "id"), and "children" is an optional service property. Now you can pass your wrapped object into the filter function alongside a condition callback:

filter(data)(item => item.step <= 2)

and you'll get your structure filtered. Here are a few more functions to deal with such structures I've just coded for fun:

const map = arr => f => {
    const res = [];
    for (const item of arr) {
        if (!item.children) {
            res.push({ ...f({ ...item }) });
        } else {
            res.push({ ...f({ ...item }), children: map(item.children)(f) });
        }
    }
    return res;
}

const reduce = arr => g => init => {
    if (!arr) return undefined;
    let res = init;
    for (const item of arr) {
        if (!item.children) {
            res = g(res)({ ...item });
        } else {
            res = g(res)({ ...item });
            res = reduce(item.children)(g)(res);
        }
    }
    return res;
}

Usage examples:

map(data)(item => ({ step: item.step }))
reduce(data)($ => item => $ + item.step)(0)

Likely, the code samples aren't ideal but probably could push someone to the right direction.

like image 35
Andrew Tatomyr Avatar answered Jun 30 '26 08:06

Andrew Tatomyr



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!