如何在对象内部执行递归reduce函数?

时间:2015-11-25 15:45:19

标签: javascript recursion reduce

我正在使用Javascript在客户端上执行此操作。我想改造:

[
  {
    "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": []
      },
      ...
    ]
  },
  ...
]

进入这个:

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

我正在练习mapreduce,所以我试图避免for循环(我做的第一件事)。这是我目前的代码:

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;
        }, []);
    },
    ...
}

所以,正如你所看到的,我不得不调用jobNewPage.buildArrayForSelect(children)来递归地执行它。我试着打电话给this.buildArrayForSelect(children),但背景不同。我觉得这不是最好的选择,因为我不想依赖于在对象中的函数内调用全局变量。我该如何改进呢?

4 个答案:

答案 0 :(得分:5)

似乎你的问题归结为如何使用函数表达式定义该函数并将其分配给更高范围的对象上的属性,从内部递归调用函数。

简单的答案是将其转换为命名函数表达式。这些函数能够递归调用自己:



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);




这种方法适用于您的对象和函数,如下所示:

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;
        }, []);
    }
}

答案 1 :(得分:2)

示例如何以递归方式将Array.prototype.reduceArray.prototype.concat结合使用。

var data = [{ "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": [] }] }];

function getAll(array) {
    return array.reduce(function (r, a) {
        r.push({ id: a.id, text: a.name });
        if (a.children && Array.isArray(a.children)) {
            r = r.concat(getAll(a.children));
        }
        return r;
    }, []);
}

document.write('<pre>' + JSON.stringify(getAll(data), 0, 4) + '</pre>');

答案 2 :(得分:1)

试试这个:

Array.prototype.flatten = function () {
    return this.reduce(function (acc, value) {
       acc.push(value);
       acc = acc.concat(value.children.flatten());
       return acc;
    }, []);
};

Array.prototype.extractData = function () {
   return this.map(function(a) {
       return (a.slug!='remote')?{'id':a.id,'text':a.name}:false
   }).filter(function(a) {
       return (a!=false)?a:false;
   });
};

提取数据:

options=array.flatten().extractData();

JsFiddle

我刚刚意识到我的答案采用了与James Thorpe的答案类似的方法(尤其是dfsq在评论中提出的解决方案)。但是,他的实施似乎远比我的有效。

答案 3 :(得分:0)

您可以将其作为立即调用的函数表达式来执行。假设您的树结构位于变量&#34; tree&#34 ;;

var tree = []; //your original tree stucture

var result = (function(input) {
    var flattened = [];

    var flattener = function(collection) {
        collection.forEach(function(item) {

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

            if (item.children.length > 0) {
                flattener(item.children);   
            }
        });
    }

    flattener(input);

    return flattened;
})(tree);

console.log(result);