深度展平JavaScript对象递归

时间:2016-04-22 20:45:37

标签: javascript recursion javascript-objects lodash

数据

var data = [
    {
      "id": 1,
      "level": "1",
      "text": "Sammy",
      "type": "Item",
      "items": [
        {
          "id": 11,
          "level": "2",
          "text": "Table",
          "type": "Item",
          "items": [
            {
              "id": 111,
              "level": "3",
              "text": "Dog",
              "type": "Item",
              "items": null
            },
            {
              "id": 112,
              "level": "3",
              "text": "Cat",
              "type": "Item",
              "items": null
            }
          ]
        },
        {
          "id": 12,
          "level": "2",
          "text": "Chair",
          "type": "Item",
          "items": [
            {
              "id": 121,
              "level": "3",
              "text": "Dog",
              "type": "Item",
              "items": null
            },
            {
              "id": 122,
              "level": "3",
              "text": "Cat",
              "type": "Item",
              "items": null
            }
          ]
        }
      ]
    },
    {
      "id": 2,
      "level": "1",
      "text": "Sundy",
      "type": "Item",
      "items": [
        {
          "id": 21,
          "level": "2",
          "text": "MTable",
          "type": "Item",
          "items": [
            {
              "id": 211,
              "level": "3",
              "text": "MTDog",
              "type": "Item",
              "items": null
            },
            {
              "id": 212,
              "level": "3",
              "text": "MTCat",
              "type": "Item",
              "items": null
            }
          ]
        },
        {
          "id": 22,
          "level": "2",
          "text": "MChair",
          "type": "Item",
          "items": [
            {
              "id": 221,
              "level": "3",
              "text": "MCDog",
              "type": "Item",
              "items": null
            },
            {
              "id": 222,
              "level": "3",
              "text": "MCCat",
              "type": "Item",
              "items": null
            }
          ]
        }
      ]
    },
    {
      "id": 3,
      "level": "1",
      "text": "Bruce",
      "type": "Folder",
      "items": [
        {
          "id": 31,
          "level": "2",
          "text": "BTable",
          "type": "Item",
          "items": [
            {
              "id": 311,
              "level": "3",
              "text": "BTDog",
              "type": "Item",
              "items": null
            },
            {
              "id": 312,
              "level": "3",
              "text": "BTCat",
              "type": "Item",
              "items": null
            }
          ]
        },
        {
          "id": 32,
          "level": "2",
          "text": "Chair",
          "type": "Item",
          "items": [
            {
              "id": 321,
              "level": "3",
              "text": "BCDog",
              "type": "Item",
              "items": null
            },
            {
              "id": 322,
              "level": "3",
              "text": "BCCat",
              "type": "Item",
              "items": null
            }
          ]
        }
      ]
    }
  ];

代码

var fdr = [];
var fd = function(n) {
  if (n.items) {
    _.forEach(n.items, function (value){
      fd(value);
    });
  }

  fdr.push(n);
};
_.forEach(data, fd);
console.log(fdr);

期望的输出

var data = [
    {
      "id": 1,
      "level": "1",
      "text": "Sammy",
      "type": "Item",
      "items": []
    },
    {
      "id": 11,
      "level": "2",
      "text": "Table",
      "type": "Item",
      "items": []
    },
    {
      "id": 111,
      "level": "3",
      "text": "Dog",
      "type": "Item",
      "items": null
    },
    {
      "id": 112,
      "level": "3",
      "text": "Cat",
      "type": "Item",
      "items": null
    },
    {
      "id": 12,
      "level": "2",
      "text": "Chair",
      "type": "Item",
      "items": []
    },
    {
      "id": 121,
      "level": "3",
      "text": "Dog",
      "type": "Item",
      "items": null
    },
    {
      "id": 122,
      "level": "3",
      "text": "Cat",
      "type": "Item",
      "items": null
    },
    {
      "id": 2,
      "level": "1",
      "text": "Sundy",
      "type": "Item",
      "items": []
    },
    {
      "id": 21,
      "level": "2",
      "text": "MTable",
      "type": "Item",
      "items": []
    },
    {
      "id": 211,
      "level": "3",
      "text": "MTDog",
      "type": "Item",
      "items": null
    },
    {
      "id": 212,
      "level": "3",
      "text": "MTCat",
      "type": "Item",
      "items": null
    },
    {
      "id": 22,
      "level": "2",
      "text": "MChair",
      "type": "Item",
      "items": []
    },
    {
      "id": 221,
      "level": "3",
      "text": "MCDog",
      "type": "Item",
      "items": null
    },
    {
      "id": 222,
      "level": "3",
      "text": "MCCat",
      "type": "Item",
      "items": null
    },
    {
      "id": 3,
      "level": "1",
      "text": "Bruce",
      "type": "Folder",
      "items": []
    },
    {
      "id": 31,
      "level": "2",
      "text": "BTable",
      "type": "Item",
      "items": []
    },
    {
      "id": 311,
      "level": "3",
      "text": "BTDog",
      "type": "Item",
      "items": null
    },
    {
      "id": 312,
      "level": "3",
      "text": "BTCat",
      "type": "Item",
      "items": null
    },
    {
      "id": 32,
      "level": "2",
      "text": "Chair",
      "type": "Item",
      "items": []
    },
    {
      "id": 321,
      "level": "3",
      "text": "BCDog",
      "type": "Item",
      "items": null
    },
    {
      "id": 322,
      "level": "3",
      "text": "BCCat",
      "type": "Item",
      "items": null
    }
  ];

条件

  • 对象有未知数级别。有些孩子item可能会降低一级,有些可能会达到5级。

问题

代码中的fd函数是我提出的。我相信有一种“更清洁”的方法可以做到这一点,只是想不出什么。另外,函数返回items对象,将其渲染为圆形对象。

JsBin: http://jsbin.com/debojiqove/2/edit?html,js,output

有没有办法用lodash或简单的JavaScript递归地展平对象?

14 个答案:

答案 0 :(得分:14)

关于项目的纯Javascript解决方案。它不会破坏源数组。

function flat(r, a) {
    var b = {};
    Object.keys(a).forEach(function (k) {
        if (k !== 'items') {
            b[k] = a[k];
        }
    });
    r.push(b);
    if (Array.isArray(a.items)) {
        b.items = a.items.map(function (a) { return a.id; });
        return a.items.reduce(flat, r);
    }
    return r;
}

var data = [{ "id": 1, "level": "1", "text": "Sammy", "type": "Item", "items": [{ "id": 11, "level": "2", "text": "Table", "type": "Item", "items": [{ "id": 111, "level": "3", "text": "Dog", "type": "Item", "items": null }, { "id": 112, "level": "3", "text": "Cat", "type": "Item", "items": null }] }, { "id": 12, "level": "2", "text": "Chair", "type": "Item", "items": [{ "id": 121, "level": "3", "text": "Dog", "type": "Item", "items": null }, { "id": 122, "level": "3", "text": "Cat", "type": "Item", "items": null }] }] }, { "id": 2, "level": "1", "text": "Sundy", "type": "Item", "items": [{ "id": 21, "level": "2", "text": "MTable", "type": "Item", "items": [{ "id": 211, "level": "3", "text": "MTDog", "type": "Item", "items": null }, { "id": 212, "level": "3", "text": "MTCat", "type": "Item", "items": null }] }, { "id": 22, "level": "2", "text": "MChair", "type": "Item", "items": [{ "id": 221, "level": "3", "text": "MCDog", "type": "Item", "items": null }, { "id": 222, "level": "3", "text": "MCCat", "type": "Item", "items": null }] }] }, { "id": 3, "level": "1", "text": "Bruce", "type": "Folder", "items": [{ "id": 31, "level": "2", "text": "BTable", "type": "Item", "items": [{ "id": 311, "level": "3", "text": "BTDog", "type": "Item", "items": null }, { "id": 312, "level": "3", "text": "BTCat", "type": "Item", "items": null }] }, { "id": 32, "level": "2", "text": "Chair", "type": "Item", "items": [{ "id": 321, "level": "3", "text": "BCDog", "type": "Item", "items": null }, { "id": 322, "level": "3", "text": "BCCat", "type": "Item", "items": null }] }] }];

document.write('<pre>' + JSON.stringify(data.reduce(flat, []), 0, 4) + '</pre>');

答案 1 :(得分:8)

带点ES6味道

function flatten(xs) {
  return xs.reduce((acc, x) => {
    acc = acc.concat(x);
    if (x.items) {
      acc = acc.concat(flatten(x.items));
      x.items = [];
    }
    return acc;
  }, []);
}

答案 2 :(得分:4)

使用reducerecursion

的简短解决方案

function flatten(data){
  return data.reduce(function(result,next){
    result.push(next);
    if(next.items){
      result = result.concat(flatten(next.items));  
      next.items = [];
    }
    return result;
  },[]);
}

var data = [
    {
      "id": 1,
      "level": "1",
      "text": "Sammy",
      "type": "Item",
      "items": [
        {
          "id": 11,
          "level": "2",
          "text": "Table",
          "type": "Item",
          "items": [
            {
              "id": 111,
              "level": "3",
              "text": "Dog",
              "type": "Item",
              "items": null
            },
            {
              "id": 112,
              "level": "3",
              "text": "Cat",
              "type": "Item",
              "items": null
            }
          ]
        },
        {
          "id": 12,
          "level": "2",
          "text": "Chair",
          "type": "Item",
          "items": [
            {
              "id": 121,
              "level": "3",
              "text": "Dog",
              "type": "Item",
              "items": null
            },
            {
              "id": 122,
              "level": "3",
              "text": "Cat",
              "type": "Item",
              "items": null
            }
          ]
        }
      ]
    },
    {
      "id": 2,
      "level": "1",
      "text": "Sundy",
      "type": "Item",
      "items": [
        {
          "id": 21,
          "level": "2",
          "text": "MTable",
          "type": "Item",
          "items": [
            {
              "id": 211,
              "level": "3",
              "text": "MTDog",
              "type": "Item",
              "items": null
            },
            {
              "id": 212,
              "level": "3",
              "text": "MTCat",
              "type": "Item",
              "items": null
            }
          ]
        },
        {
          "id": 22,
          "level": "2",
          "text": "MChair",
          "type": "Item",
          "items": [
            {
              "id": 221,
              "level": "3",
              "text": "MCDog",
              "type": "Item",
              "items": null
            },
            {
              "id": 222,
              "level": "3",
              "text": "MCCat",
              "type": "Item",
              "items": null
            }
          ]
        }
      ]
    },
    {
      "id": 3,
      "level": "1",
      "text": "Bruce",
      "type": "Folder",
      "items": [
        {
          "id": 31,
          "level": "2",
          "text": "BTable",
          "type": "Item",
          "items": [
            {
              "id": 311,
              "level": "3",
              "text": "BTDog",
              "type": "Item",
              "items": null
            },
            {
              "id": 312,
              "level": "3",
              "text": "BTCat",
              "type": "Item",
              "items": null
            }
          ]
        },
        {
          "id": 32,
          "level": "2",
          "text": "Chair",
          "type": "Item",
          "items": [
            {
              "id": 321,
              "level": "3",
              "text": "BCDog",
              "type": "Item",
              "items": null
            },
            {
              "id": 322,
              "level": "3",
              "text": "BCCat",
              "type": "Item",
              "items": null
            }
          ]
        }
      ]
    }
  ];

var result = flatten(data);

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

答案 3 :(得分:3)

以下是使用递归函数的解决方案,我称之为function flattenNestedObjectsArray(arr, part){ var flattened = part || [], items; arr.forEach(function(v){ if (Array.isArray(v.items) && v.items.length) { items = v.items; v.items = []; flattened.push(v); flattened.concat(flattened, flattenNestedObjectsArray(items, flattened)); } else { flattened.push(v); } }); return flattened; } var flattened = flattenNestedObjectsArray(data); console.log(JSON.stringify(flattened, 0, 4)); (对于本机JavaScript ):

console.log

[ { "id": 1, "level": "1", "text": "Sammy", "type": "Item", "items": [] }, { "id": 11, "level": "2", "text": "Table", "type": "Item", "items": [] }, { "id": 111, "level": "3", "text": "Dog", "type": "Item", "items": null }, { "id": 112, "level": "3", "text": "Cat", "type": "Item", "items": null }, { "id": 12, "level": "2", "text": "Chair", "type": "Item", "items": [] }, { "id": 121, "level": "3", "text": "Dog", "type": "Item", "items": null }, { "id": 122, "level": "3", "text": "Cat", "type": "Item", "items": null }, { "id": 2, "level": "1", "text": "Sundy", "type": "Item", "items": [] }, { "id": 21, "level": "2", "text": "MTable", "type": "Item", "items": [] }, { "id": 211, "level": "3", "text": "MTDog", "type": "Item", "items": null }, { "id": 212, "level": "3", "text": "MTCat", "type": "Item", "items": null }, { "id": 22, "level": "2", "text": "MChair", "type": "Item", "items": [] }, { "id": 221, "level": "3", "text": "MCDog", "type": "Item", "items": null }, { "id": 222, "level": "3", "text": "MCCat", "type": "Item", "items": null }, { "id": 3, "level": "1", "text": "Bruce", "type": "Folder", "items": [] }, { "id": 31, "level": "2", "text": "BTable", "type": "Item", "items": [] }, { "id": 311, "level": "3", "text": "BTDog", "type": "Item", "items": null }, { "id": 312, "level": "3", "text": "BTCat", "type": "Item", "items": null }, { "id": 32, "level": "2", "text": "Chair", "type": "Item", "items": [] }, { "id": 321, "level": "3", "text": "BCDog", "type": "Item", "items": null }, { "id": 322, "level": "3", "text": "BCCat", "type": "Item", "items": null } ] 输出:

modal.PersonInfo

答案 4 :(得分:3)

普通JavaScript

var data = [{ "id": 1, "level": "1", "text": "Sammy", "type": "Item", "items": [{ "id": 11, "level": "2", "text": "Table", "type": "Item", "items": [{ "id": 111, "level": "3", "text": "Dog", "type": "Item", "items": null }, { "id": 112, "level": "3", "text": "Cat", "type": "Item", "items": null }] }, { "id": 12, "level": "2", "text": "Chair", "type": "Item", "items": [{ "id": 121, "level": "3", "text": "Dog", "type": "Item", "items": null }, { "id": 122, "level": "3", "text": "Cat", "type": "Item", "items": null }] }] }, { "id": 2, "level": "1", "text": "Sundy", "type": "Item", "items": [{ "id": 21, "level": "2", "text": "MTable", "type": "Item", "items": [{ "id": 211, "level": "3", "text": "MTDog", "type": "Item", "items": null }, { "id": 212, "level": "3", "text": "MTCat", "type": "Item", "items": null }] }, { "id": 22, "level": "2", "text": "MChair", "type": "Item", "items": [{ "id": 221, "level": "3", "text": "MCDog", "type": "Item", "items": null }, { "id": 222, "level": "3", "text": "MCCat", "type": "Item", "items": null }] }] }, { "id": 3, "level": "1", "text": "Bruce", "type": "Folder", "items": [{ "id": 31, "level": "2", "text": "BTable", "type": "Item", "items": [{ "id": 311, "level": "3", "text": "BTDog", "type": "Item", "items": null }, { "id": 312, "level": "3", "text": "BTCat", "type": "Item", "items": null }] }, { "id": 32, "level": "2", "text": "Chair", "type": "Item", "items": [{ "id": 321, "level": "3", "text": "BCDog", "type": "Item", "items": null }, { "id": 322, "level": "3", "text": "BCCat", "type": "Item", "items": null }] }] }];

var r = [];

function flatten(a) {
    if (a.length == 0) return;
    var o = {};
    o.id = a[0].id;
    o.level = a[0].level;
    o.text = a[0].text;
    o.type = a[0].type
    o.items = a[0].items == null ? null : []
    r.push(o);
    if (Array.isArray(a[0].items)) {
        flatten(a[0].items);
    }
    a.shift();
    flatten(a);
}

flatten(data);

document.write('<pre>' + JSON.stringify(r, 0, 2) + '</pre>');

答案 5 :(得分:2)

这是我的递归flattenItems函数的版本。 请注意,我已删除最终结果中所有级别的属性。

function flattenItems(data) {
    // flat is the array that we will return by the end
    var flat = [];
    data.forEach(function(item) {
        // get child properties only
        var flatItem = {};
        Object.keys(item).forEach(function(key) {
            if(item[key] && item.hasOwnProperty(key) && !Array.isArray(item[key])) {
                flatItem[key] = item[key];
            }
            // recursive flattern on subitems
            // add recursive call results to the 
            // current stack version of "flat", by merging arrays
            else if(Array.isArray(item[key])) {
                Array.prototype.push.apply(flat, flattenItems(item[key]));
            }
        });
        flat.push(flatItem);
    });
    // sort by level before returning
    return flat.sort(function(i1, i2) {
        return parseInt(i1.level) - parseInt(i2.level);
    });
  }

这是使用您的示例数据的fiddle,请检查控制台。

答案 6 :(得分:2)

只有一个班轮功能才能完成这项工作。

&#13;
&#13;
var data = [{ "id": 1, "level": "1", "text": "Sammy", "type": "Item", "items": [{ "id": 11, "level": "2", "text": "Table", "type": "Item", "items": [{ "id": 111, "level": "3", "text": "Dog", "type": "Item", "items": null }, { "id": 112, "level": "3", "text": "Cat", "type": "Item", "items": null }] }, { "id": 12, "level": "2", "text": "Chair", "type": "Item", "items": [{ "id": 121, "level": "3", "text": "Dog", "type": "Item", "items": null }, { "id": 122, "level": "3", "text": "Cat", "type": "Item", "items": null }] }] }, { "id": 2, "level": "1", "text": "Sundy", "type": "Item", "items": [{ "id": 21, "level": "2", "text": "MTable", "type": "Item", "items": [{ "id": 211, "level": "3", "text": "MTDog", "type": "Item", "items": null }, { "id": 212, "level": "3", "text": "MTCat", "type": "Item", "items": null }] }, { "id": 22, "level": "2", "text": "MChair", "type": "Item", "items": [{ "id": 221, "level": "3", "text": "MCDog", "type": "Item", "items": null }, { "id": 222, "level": "3", "text": "MCCat", "type": "Item", "items": null }] }] }, { "id": 3, "level": "1", "text": "Bruce", "type": "Folder", "items": [{ "id": 31, "level": "2", "text": "BTable", "type": "Item", "items": [{ "id": 311, "level": "3", "text": "BTDog", "type": "Item", "items": null }, { "id": 312, "level": "3", "text": "BTCat", "type": "Item", "items": null }] }, { "id": 32, "level": "2", "text": "Chair", "type": "Item", "items": [{ "id": 321, "level": "3", "text": "BCDog", "type": "Item", "items": null }, { "id": 322, "level": "3", "text": "BCCat", "type": "Item", "items": null }] }] }],

flatIron = (a,b) => a.reduce((p,c) => {!!c.items ? (p.push(c), flatIron(c.items,p), c.items = []) : p.push(c); return p},b),
 flatArr = flatIron(data,[]);

document.write('<pre>' + JSON.stringify(flatArr, 0, 2) + '</pre>');
&#13;
&#13;
&#13;

答案 7 :(得分:2)

使用递归reducer函数的另一种方法

 _.reduce(data, function reducer(result, val) {
     var items = _.reduce(val.items, reducer, []);
     val.items = _.isArray(val.items) ? [] : val.items;
     return _.concat(result, val, items);
 }, []);

答案 8 :(得分:2)

我需要做同样的事情,在解决我的问题的同时找到了使用lodash的解决方案:

$(document).ready(function(){ 
function switchTab() {
var p = $(this).parent('li');
var i  = p.index();
var s = section.eq(i);
var c = s.find('*');

section.removeClass('active');
sectionContent.removeAttr('style');
s.addClass('active');

c.css({
    transform: 'none',
    opacity: 1
});

linkParent.removeClass('active');
p.addClass('active');

return false;
};
});

答案 9 :(得分:1)

自Lo-Dash 3.0.0起,_.flattenDeep(data)将根据需要返回深度扁平化的数组。还有一个_.flatten(数据)函数可以浅显扁平化。

答案 10 :(得分:1)

修改后的РоманПарадеев答案使其更具动态感。

function flatten(xs, childSelector) {
  return xs.reduce((acc, x) => {
    acc = acc.concat(x);
    let children = childSelector(x);
    if (children) {
      acc = acc.concat(flatten(children, childSelector));
    }
    return acc;
  }, []);
}

现在items未进行硬编码,您可以使用它flatten(data, x => x.items)

答案 11 :(得分:1)

使用_.flatMapDeep(自Lodash 4.7起可用):

var flatten = function(item) {
  return [item, _.flatMapDeep(item.items, flatten)];
}

var result = _.flatMapDeep(data, flatten);

答案 12 :(得分:0)

此解决方案应在IE11上有效;使用过滤,映射和缩小。

var item = function(x) {
    return {
        "id": x.id,
        "level": x.level,
        "text": x.text,
        "type": x.type,
        "items": x.items ? [] : null
    }
}

var flatten = function(a, b) {
    return a.concat(b);
};

var onlyUnique = function(acc, curr) {
    if (acc.length == 0) {
        acc.push(curr);
    } else {
        var search = acc.filter(function(x){return x.id===curr.id;})
        if (search.length == 0) {
            acc.push(curr);
        }
    }
    return acc;
}

var newData = data.map(function(x) {

    return x.items.map(function(xx) {
        return xx.items.map(function(xxx) {
            return [item(x), item(xx), item(xxx)];
        }).reduce(flatten, []);


    }).reduce(flatten, [])



}).reduce(flatten, []).reduce(onlyUnique, []);;

console.log(JSON.stringify(newData, null, 2))

答案 13 :(得分:0)

由于这个较旧的问题已被提出,这里是一个现代版本。它不会改变(@Nina Scholz:或破坏!)原始数据,而是返回一个包含新对象的新数组。

const flattenItems = (xs) =>
  xs. flatMap (({items, ... node}) => [node, ... flattenItems (items || [])])

const data = [{id: 1, level: "1", text: "Sammy", type: "Item", items: [{id: 11, level: "2", text: "Table", type: "Item", items: [{id: 111, level: "3", text: "Dog", type: "Item", items: null}, {id: 112, level: "3", text: "Cat", type: "Item", items: null}]}, {id: 12, level: "2", text: "Chair", type: "Item", items: [{id: 121, level: "3", text: "Dog", type: "Item", items: null}, {id: 122, level: "3", text: "Cat", type: "Item", items: null}]}]}, {id: 2, level: "1", text: "Sundy", type: "Item", items: [{id: 21, level: "2", text: "MTable", type: "Item", items: [{id: 211, level: "3", text: "MTDog", type: "Item", items: null}, {id: 212, level: "3", text: "MTCat", type: "Item", items: null}]}, {id: 22, level: "2", text: "MChair", type: "Item", items: [{id: 221, level: "3", text: "MCDog", type: "Item", items: null}, {id: 222, level: "3", text: "MCCat", type: "Item", items: null}]}]}, {id: 3, level: "1", text: "Bruce", type: "Folder", items: [{id: 31, level: "2", text: "BTable", type: "Item", items: [{id: 311, level: "3", text: "BTDog", type: "Item", items: null}, {id: 312, level: "3", text: "BTCat", type: "Item", items: null}]}, {id: 32, level: "2", text: "Chair", type: "Item", items: [{id: 321, level: "3", text: "BCDog", type: "Item", items: null}, {id: 322, level: "3", text: "BCCat", type: "Item", items: null}]}]}]

console .log (flattenItems (data))
.as-console-wrapper {max-height: 100% !important; top: 0}

此版本不包含相当无用的 items 属性。添加它很容易,就像这样:

const flattenItems = (xs) =>
  xs. flatMap (({items, ... node}) => [
    {... node, items: items == null ? null : []}, 
    ... flattenItems (items || [])
  ])