web-dev-qa-db-ja.com

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 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は1レベル下の場合もあれば、最大5レベルの場合もあります。

質問

コードのfd関数は、私が思いついたものです。これを行うには「よりクリーンな」方法があると思いますが、何かを考えることはできません。さらに、関数はitemsオブジェクトを返し、それを円形オブジェクトにレンダリングします。

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

Lodashまたは単純なJavaScriptでオブジェクトを再帰的にフラット化する方法はありますか?.

18
stack247

アイテムに関するプレーン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>');
14
Nina Scholz

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

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>');
4
Bhabishya Kumar

私は同じことをする必要があり、私の問題を解決している間にlodashを使用してあなたの解決策を見つけました:

function kids(node) {
    return node.items
        ? [{...node, items: []}, _.map(node.items, kids)]
        : {...node, items: null};
}

_.flatMapDeep(data, kids);
3
Paul

プレーン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>');
3
isvforall

これは、flattenNestedObjectsArray()(for native JavaScript)を呼び出した再帰関数を使用した解決策です。

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

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
    }
]
3
RomanPerekhrest

_.flatMapDeepの使用(Lodash 4.7以降で利用可能):

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

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

単一のライナー機能だけがこの仕事をすることができます。

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>');
2
Redu

再帰的なレデューサー関数を持つ別の方法

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

これが再帰的なflattenItems関数の私のバージョンです。最終結果のすべてのレベルでitemsプロパティを削除したことに注意してください。

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 であり、サンプルデータを使用して、コンソールを確認します。

2
Freeman Lambda

SomewhatоманПарадеевの回答を変更して、やや動的にする。

_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)を使用できるようになりました。

1
Peter

Lo-Dash 3.0.0以降、_。flattenDeep(data)は、必要に応じて深くフラット化された配列を返します。浅く平坦化する_.flatten(data)関数もあります。

1
Jonathan

このソリューションはIE11で動作するはずです。 filter、map、reduceを使用します。

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))
0
Mikyas Gami