Search code examples
javascriptsearchtree

How to filter tree-like named object?


I have a tree object like below, and I need to search through it. I know there's already some answers on topics like this here, but they are, unfortunately, do not comply with my structure.

const tree = {
    "28": {
        "label": "lorem"
        "children": {
            "188": {
                "label": "ipsum"
                "children": {
                    "482": {
                        "label": "fish"
                        "children": {
                            "185": {
                                "label": "dog"
                            },
                            "289": {
                                "label": "cat"
                            }
                        }
                    }
                }
            }
        }
    },
    "33": {
        "label": "water"
        "children": {
            "95": {
                "label": "fire"
                "children": {
                    "181": {
                        "label": "gas"
                        "children": {
                            "100": {
                                "label": "station"
                            }
                        }
                    }
                    "182": {
                        "label": ""
                        "children": {
                            "100": {
                                "label": "sushi"
                            }
                        }
                    }
                }
            }
        }
    }
}

For example, if I'll search for 'fish', output should be like this:

{
    "28": {
        "label": "lorem"
        "children": {
            "188": {
                "label": "ipsum"
                "children": {
                    "482": {
                        "label": "fish"
                        "children": {
                            "185": {
                                "label": "dog"
                            },
                            "289": {
                                "label": "cat"
                            }
                        }
                    }
                }
            }
        }
    }
}

And if I'll search for 'dog' instead:

{
    "28": {
        "label": "lorem"
        "children": {
            "188": {
                "label": "ipsum"
                "children": {
                    "482": {
                        "label": "fish"
                        "children": {
                            "185": {
                                "label": "dog"
                            }
                        }
                    }
                }
            }
        }
    }
}

I tried to use these answers, but couldn't adapt them to named objects:

How to filter a tree structure while keeping the decendants of the parents that are filtered out?

A Javascript function to filter tree structured json with a search term. exclude any object which donot match the search term


Solution

    1. Use a recursive function that searches in the children property recursively.
    2. Since you mentioned "filter", we should support finding multiple results.
    3. For more flexibility we could provide a callback like in Array::filter().
    4. Note that we don't use here spread syntax and functional approach since it would be slower.

    const search = (obj, cb) => {
    
      let out = null;
    
      for(const key in obj){
      
        const item = obj[key];
        
        if(cb(item)){
          (out ??= {})[key] = item;
          continue;
        }
        
        if(item.children){
          const found = search(item.children, cb);
          if(found){
            (out ??= {})[key] = Object.assign({}, item, {children: found});
          }
        }
      }
      
      return out;
    
    };
    
    [
      item => item.label === 'fish',
      item => item.label === 'dog',
      item => ['dog', 'station'].includes(item.label),
      item => false
    ].forEach(cb => $pre.insertAdjacentHTML('beforeend', cb + ': ' + JSON.stringify(search(tree, cb), null, 4) + '\n'));
    <script>
    const tree = {
        "28": {
            "label": "lorem",
            "children": {
                "188": {
                    "label": "ipsum",
                    "children": {
                        "482": {
                            "label": "fish",
                            "children": {
                                "185": {
                                    "label": "dog"
                                },
                                "289": {
                                    "label": "cat"
                                }
                            }
                        }
                    }
                }
            }
        },
        "33": {
            "label": "water",
            "children": {
                "95": {
                    "label": "fire",
                    "children": {
                        "181": {
                            "label": "gas",
                            "children": {
                                "100": {
                                    "label": "station"
                                }
                            }
                        },
                        "182": {
                            "label": "",
                            "children": {
                                "100": {
                                    "label": "sushi"
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    </script>
    <pre id="$pre"></pre>