Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I recursively use Array.prototype.find() while returning a single object?

The bigger problem I am trying to solve is, given this data:

var data = [
    { id: 1 },
    { id: 2 },
    { id: 3 },
    { id: 4, children: [
        { id: 6 },
        { id: 7, children: [
            {id: 8 },
            {id: 9 }
        ]}
    ]},
    { id: 5 }
]

I want to make a function findById(data, id) that returns { id: id }. For example, findById(data, 8) should return { id: 8 }, and findById(data, 4) should return { id: 4, children: [...] }.

To implement this, I used Array.prototype.find recursively, but ran into trouble when the return keeps mashing the objects together. My implementation returns the path to the specific object.

For example, when I used findById(data, 8), it returns the path to { id: 8 }:

 { id: 4, children: [ { id: 6 }, { id: 7, children: [ { id: 8}, { id: 9] } ] }

Instead I would like it to simply return

{ id: 8 }

Implementation (Node.js v4.0.0)

jsfiddle

var data = [
    { id: 1 },
    { id: 2 },
    { id: 3 },
    { id: 4, children: [
        { id: 6 },
        { id: 7, children: [
            {id: 8 },
            {id: 9 }
        ]}
    ]},
    { id: 5 }
]

function findById(arr, id) {
    return arr.find(a => {
        if (a.children && a.children.length > 0) {
            return a.id === id ? true : findById(a.children, id)
        } else {
            return a.id === id
        }
    })
    return a
}

console.log(findById(data, 8)) // Should return { id: 8 }

// Instead it returns the "path" block: (to reach 8, you go 4->7->8)
//
// { id: 4,
//   children: [ { id: 6 }, { id: 7, children: [ {id: 8}, {id: 9] } ] }
like image 491
Keith Yong Avatar asked Apr 28 '16 12:04

Keith Yong


1 Answers

The problem what you have, is the bubbling of the find. If the id is found inside the nested structure, the callback tries to returns the element, which is interpreted as true, the value for the find.

The find method executes the callback function once for each element present in the array until it finds one where callback returns a true value. [MDN]

Instead of find, I would suggest to use a recursive style for the search with a short circuit if found.

var data = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4, children: [{ id: 6 }, { id: 7, children: [{ id: 8 }, { id: 9 }] }] }, { id: 5 }];

function findById(data, id) {
    function iter(a) {
        if (a.id === id) {
            result = a;
            return true;
        }
        return Array.isArray(a.children) && a.children.some(iter);
    }

    var result;
    data.some(iter);
    return result
}

console.log(findById(data, 8));
like image 116
Nina Scholz Avatar answered Sep 20 '22 07:09

Nina Scholz