4
\$\begingroup\$

I wrote the following code as a way of plucking data from a n-depth JavaScript object. It works a charm and even appends the parents of the the found item.

I was just wondering if you guys had any ideas on making it better.

//#region function recursiveFind
function recursiveFind(data, key, childKey, match, result) {

    ///<summary>Returns JS obj and its parents based on a data set, key and match.</summary>
    ///<param name="data" type="Object" optional="false">the data object you want to find within</param>
    ///<param name="key" type="String" optional="false">the key for the data you want to match against eg. 'id' will look for data.id</param>
    ///<param name="childKey" type="String" optional="false">the data object you want to find within</param>
    ///<param name="match" type="Any" optional="false">the value you wish to search for</param>
    ///<returns type="Object">returns the found result</returns>

    var parents = [];
    var depth = 0;
    var recurse = function (data, key, childKey, match, result) {
        // Build Parents
        depth++;

        // Check this Object
        if (data[key] === match) {
            result = data;
        }
        else if (depth === 1) {
            parents.push(data);

        }

        // If not found check children
        if (result === undefined) {
            if (data[childKey]) {
                $.each(data[childKey], function (k, v) {
                    if (v[key] === match) {
                        result = v;
                        return false;
                    } else {
                        result = recurse(v, key, childKey, match, result);
                        if (result) {
                            parents.push(v);
                            return false;
                        }
                    }
                });
            }
        }

        // Set parents object into the result
        if (result) {
            result.parents = $.extend([], parents);
        }

        // Clean up parents object
        depth--;
        if (depth === 0) {
            parents.length = 0;
        }
        return result;
    };
    return recurse(data, key, childKey, match, result);
}
//#endregion
\$\endgroup\$
3
  • \$\begingroup\$ You you add an example of use? \$\endgroup\$
    – plalx
    Commented Oct 11, 2013 at 20:13
  • \$\begingroup\$ Are you sure ` parents.length = 0;` works? \$\endgroup\$
    – konijn
    Commented Dec 16, 2013 at 19:52
  • \$\begingroup\$ @tomdemuyt yep, it is the preferred way of reseting an array in JavaScript stackoverflow.com/questions/1232040/… \$\endgroup\$
    – Blowsie
    Commented Dec 17, 2013 at 10:58

1 Answer 1

6
\$\begingroup\$

Overal

I think the code looks too busy just to find a key/value match in an object tree-structure, it took a while to figure out which part(s) bothered me.

Doing it twice

You are doing the matching twice, once with (data[key] === match) and once with (v[key] === match), it would be cleaner to leave the second check to recursion.

Function in a function

I am assuming you use a function in a function to keep track of depth and parents, which isn't worth it. You can do this in a single function with an optional parameter.

Naming

  • match -> matchValue because match sounds like a boolean to me
  • childKey - childrenKey because the key really points to an array
  • result.parents -> avoid this, what if the object already has a property called parents ?

Counter proposal

If you think about it, result can be considered the parent of key, so I would build a function that returns all parents in an array, if you wish you can then have recursiveFind call that.

function findParents( data, key, matchValue, childrenKey, parents )
{
  var i , children , childrenCount;
  /* Prevent the need for an inner function by re-passing parents */
  parents = parents || [];
  parents.push( data )
  /* Return immediately if we find a match */
  if( data[key] === matchValue )
    return parents.reverse(); 
  /* Are there children to inspect ? */
  if( !data[childrenKey] || !data[childrenKey].length )
    return false;
  children = data[childrenKey];
  childrenCount = children.length;
  /* See if any children have it ? */
  for( i = 0 ; i < childrenCount ; i++ )
  {
    if( parents = findParents( children[i] , key , matchValue , childrenKey , parents.slice() ) )
      return parents;
  }
  /* Fail.. */
  return false;
}

Then recursiveFind becomes

function recursiveFind(data, key, childrenKey, match )
{
  var parents = findParents( data , key, match ),
      result = parents.shift()
  result.parents = parents;
  return result;
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.