Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to do a recursive reduce function inside an object?

I am doing this on the client with Javascript. I want to transform:

[
  {
    "id": 10,
    "name": "Designer",
    "slug": "designer",
    "children": [
      {
        "id": 11,
        "name": "UI / Visual Designer",
        "slug": "ui-visual-designer",
        "children": []
      },
      ...
    ]
  },
  {
    "id": 1,
    "name": "Software Engineer",
    "slug": "software-engineer",
    "children": [
      {
        "id": 2,
        "name": "Back-End Developer",
        "slug": "back-end-developer",
        "children": []
      },
      ...
    ]
  },
  ...
]

into this:

[
  {
    "id": 10,
    "text": "Designer"
  },
  {
    "id": 11,
    "text": "UI / Visual Designer",
  },
  {
    "id": 1,
    "text": "Software Engineer",
  },
  {
    "id": 2,
    "text": "Back-End Developer",
  }
  ...
]

I am practicing with map and reduce so I am trying to avoid for loops (first thing I did). This is the current code I have:

var jobNewPage = {
    ...
    buildArrayForSelect(array) {
        "use strict";
        return $.extend(true, [], array).reduce(function(total, item) {
            if ( item.slug == 'remote' ) return total;

            total.push({
                'id'   : item.id,
                'text' : item.name
            });

            let children = item.children;
            if (children && children.length) {
                // TODO: We had to call the global context jobNewPage
                total = total.concat(jobNewPage.buildArrayForSelect(children));
            }
            return total;
        }, []);
    },
    ...
}

So, as you can see, I have had to call jobNewPage.buildArrayForSelect(children) to do it recursively. I tried to call this.buildArrayForSelect(children) but the context is different. I feel this is not the best option because I don't want to depend calling a global variable inside a function in the object. How can I improve it?

like image 572
fesja Avatar asked Nov 25 '15 15:11

fesja


People also ask

Is reduce () a recursive function?

reduce(function(done,curr){if the previous line is true and the argument is an array, we want to reduce it. This is our recursive case.

Does reduce work on object?

reduce will return the only value (object or otherwise) if the list object only has one item, without calling iterator function.

How do you stop a recursive function?

To prevent infinite recursion, you can use if...else statement (or similar approach) where one branch makes the recursive call, and the other doesn't. So, it generally looks like this. function recurse() { if(condition) { recurse(); } else { // stop calling recurse() } } recurse();

How do you reduce to create an object?

To create an array (or object) using reduce, we first must pass in an empty array (or object if we see fit) as the initial value. Then within each step of reduce, we perform the desired operation on the passed in array (or object) and return the newly mutated array (or object).


Video Answer


1 Answers

It seems your question boils down to how to recursively call a function from within itself, when that function is defined using a function expression and assigned to a property on a higher-scoped object.

The simple answer is to turn it into a named function expression. Such functions are are able to call themselves recursively:

var obj = {
   myMethod: function myName(n) { //function expression has name "myName"...
     console.log(n);
     if (n > 0)
       myName(n-1); //...which we can use inside the function...
   }
}

//...and outside we refer to the object's property name
obj.myMethod(5);

This approach, applied to your object and function, would look as follows:

var jobNewPage = {
    //give the function expression a name:
    buildArrayForSelect: function buildArrayForSelect(array) {
        "use strict";
        return $.extend(true, [], array).reduce(function(total, item) {
            if ( item.slug == 'remote' ) return total;

            total.push({
                'id'   : item.id,
                'text' : item.name
            });

            let children = item.children;
            if (children && children.length) {
                //No need to reference object to call the function recursively:
                total = total.concat(buildArrayForSelect(children));
            }
            return total;
        }, []);
    }
}
like image 119
James Thorpe Avatar answered Oct 06 '22 23:10

James Thorpe