递归循环遍历对象以构建属性列表

Jak*_*ake 50 javascript object

情况:我有一个包含多个子和子子对象的大对象,其属性包含多种数据类型.出于我们的目的,此对象看起来像这样:

var object = {
    aProperty: {
        aSetting1: 1,
        aSetting2: 2,
        aSetting3: 3,
        aSetting4: 4,
        aSetting5: 5
    },
    bProperty: {
        bSetting1: {
            bPropertySubSetting : true
        },
        bSetting2: "bString"
    },
    cProperty: {
        cSetting: "cString"
    }
}
Run Code Online (Sandbox Code Playgroud)

我需要循环遍历此对象并构建显示层次结构的键列表,因此列表最终看起来像这样:

aProperty.aSetting1
aProperty.aSetting2
aProperty.aSetting3
aProperty.aSetting4
aProperty.aSetting5
bProperty.bSetting1.bPropertySubSetting
bProperty.bSetting2
cProperty.cSetting
Run Code Online (Sandbox Code Playgroud)

我有这个函数,它循环遍历对象并吐出密钥,但不是分层次的:

function iterate(obj) {
    for (var property in obj) {
        if (obj.hasOwnProperty(property)) {
            if (typeof obj[property] == "object") {
                iterate(obj[property]);
            }
            else {
                console.log(property + "   " + obj[property]);
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

有人能告诉我怎么做吗?这里有一个jsfiddle让你搞砸:http://jsfiddle.net/tbynA/

Art*_*oev 103

我为你做了一个FIDDLE.我存储一个stack字符串,然后输出它,如果该属性是基本类型:

function iterate(obj, stack) {
        for (var property in obj) {
            if (obj.hasOwnProperty(property)) {
                if (typeof obj[property] == "object") {
                    iterate(obj[property], stack + '.' + property);
                } else {
                    console.log(property + "   " + obj[property]);
                    $('#output').append($("<div/>").text(stack + '.' + property))
                }
            }
        }
    }

iterate(object, '')
Run Code Online (Sandbox Code Playgroud)

更新(2017年7月24日) - 不要使用,它被窃听

最近我收到了很多关于这个问题的赞成票,所以我决定用一些ES2015 +魔术和更多功能风格来改进解决方案.

它可能不太可读,但我喜欢它的样子:)你仍然可以使用上面更简单的解决方案 - 这两者应该完全相同.

const isObject = val =>
  typeof val === 'object' && !Array.isArray(val);

const paths = (obj = {}) =>
  Object.entries(obj)
    .reduce(
      (product, [key, value]) =>
        isObject(value) ?
        product.concat([
          [key, paths(value)] // adds [root, [children]] list
        ]) :
        product.concat([key]), // adds [child] list
      []
    )

const addDelimiter = (a, b) =>
  a ? `${a}.${b}` : b;

const pathToString = ([root, children]) =>
  children.map(
    child =>
      Array.isArray(child) ?
      addDelimiter(root, pathToString(child)) :
      addDelimiter(root, child)
  )
  .join('\n');

const input = {
  aProperty: {
    aSetting1: 1,
    aSetting2: 2,
    aSetting3: 3,
    aSetting4: 4,
    aSetting5: 5
  },
  bProperty: {
    bSetting1: {
      bPropertySubSetting: true
    },
    bSetting2: "bString"
  },
  cProperty: {
    cSetting: "cString"
  }
};

// ^ implies a "root" level will be ["", paths(input)]
// ideally paths() should return that structure, but I could not figure out how :)
// shows desired output format
console.log(pathToString(["", paths(input)]));

// showcase the resulting data structure
// any object can be recursively represented as a list [objectPropertyName, [...nestedPropertyNames]]
// console.log(paths(input));
Run Code Online (Sandbox Code Playgroud)

  • 我想要你的代码婴儿......如果这是一件事..child()也许?谢谢. (20认同)

小智 53

Artyom Neustroev 的解决方案不适用于复杂的对象,因此这里有一个基于他的想法的可行解决方案:

function propertiesToArray(obj) {
    const isObject = val =>
        typeof val === 'object' && !Array.isArray(val);

    const addDelimiter = (a, b) =>
        a ? `${a}.${b}` : b;

    const paths = (obj = {}, head = '') => {
        return Object.entries(obj)
            .reduce((product, [key, value]) => 
                {
                    let fullPath = addDelimiter(head, key)
                    return isObject(value) ?
                        product.concat(paths(value, fullPath))
                    : product.concat(fullPath)
                }, []);
    }

    return paths(obj);
}
Run Code Online (Sandbox Code Playgroud)

  • 对于那些只想要值的人:product.concat(value) 而不是:product.concat(fullPath) (3认同)
  • 好的!只是一个观察。您应该检查“isObject”箭头函数中的“null”。因为`typeof null = 'object'`。 (2认同)
  • 仅当对象中存在 null 时出现一些错误,效果很好。所以这对我有用: return Object.entries(obj || {}) (2认同)

Ond*_*dar 15

如果对象在其对象图中有循环,则会遇到问题,例如:

var object = {
    aProperty: {
        aSetting1: 1
    },
};
object.ref = object;
Run Code Online (Sandbox Code Playgroud)

在这种情况下,您可能希望保留已经完成的对象的引用并将它们从迭代中排除.

如果对象图太深,你也会遇到问题:

var object = {
  a: { b: { c: { ... }} }
};
Run Code Online (Sandbox Code Playgroud)

你会得到太多的递归调用错误.两者都可以避免:

function iterate(obj) {
    var walked = [];
    var stack = [{obj: obj, stack: ''}];
    while(stack.length > 0)
    {
        var item = stack.pop();
        var obj = item.obj;
        for (var property in obj) {
            if (obj.hasOwnProperty(property)) {
                if (typeof obj[property] == "object") {
                  var alreadyFound = false;
                  for(var i = 0; i < walked.length; i++)
                  {
                    if (walked[i] === obj[property])
                    {
                      alreadyFound = true;
                      break;
                    }
                  }
                  if (!alreadyFound)
                  {
                    walked.push(obj[property]);
                    stack.push({obj: obj[property], stack: item.stack + '.' + property});
                  }
                }
                else
                {
                    console.log(item.stack + '.' + property + "=" + obj[property]);
                }
            }
        }
    }
}

iterate(object); 
Run Code Online (Sandbox Code Playgroud)


Mat*_*ula 11

https://github.com/hughsk/flat

var flatten = require('flat')
flatten({
key1: {
    keyA: 'valueI'
},
key2: {
    keyB: 'valueII'
},
key3: { a: { b: { c: 2 } } }
})

// {
//   'key1.keyA': 'valueI',
//   'key2.keyB': 'valueII',
//   'key3.a.b.c': 2
// }
Run Code Online (Sandbox Code Playgroud)

只需循环获取索引.


Jac*_*fin 8

你不需要递归!

以下函数函数将按从最深到最深的顺序输出条目,并将键的值作为[key, value]数组输出。

function deepEntries( obj ){
    'use-strict';
    var allkeys, curKey = '[', len = 0, i = -1, entryK;

    function formatKeys( entries ){
       entryK = entries.length;
       len += entries.length;
       while (entryK--)
         entries[entryK][0] = curKey+JSON.stringify(entries[entryK][0])+']';
       return entries;
    }
    allkeys = formatKeys( Object.entries(obj) );

    while (++i !== len)
        if (typeof allkeys[i][1] === 'object' && allkeys[i][1] !== null){
            curKey = allkeys[i][0] + '[';
            Array.prototype.push.apply(
                allkeys,
                formatKeys( Object.entries(allkeys[i][1]) )
            );
        }
    return allkeys;
}
Run Code Online (Sandbox Code Playgroud)

然后,要输出您正在寻找的结果类型,只需使用它。

function stringifyEntries(allkeys){
    return allkeys.reduce(function(acc, x){
        return acc+((acc&&'\n')+x[0])
    }, '');
};
Run Code Online (Sandbox Code Playgroud)

如果您对技术位感兴趣,那么这就是它的工作原理。它的工作原理是获取您传递Object.entriesobj对象的 并将它们放入数组中allkeys。然后,从开始allkeys到结束,如果它发现allkeys条目值之一是一个对象,则它获取该条目的键为curKey,并curKey在将结果数组推送到结束之前为其每个自己的条目键添加前缀allkeys。然后,它将添加allkeys到目标长度的条目数添加到目标长度,以便它也将遍历那些新添加的键。

例如,请注意以下事项:

function deepEntries( obj ){
    'use-strict';
    var allkeys, curKey = '[', len = 0, i = -1, entryK;

    function formatKeys( entries ){
       entryK = entries.length;
       len += entries.length;
       while (entryK--)
         entries[entryK][0] = curKey+JSON.stringify(entries[entryK][0])+']';
       return entries;
    }
    allkeys = formatKeys( Object.entries(obj) );

    while (++i !== len)
        if (typeof allkeys[i][1] === 'object' && allkeys[i][1] !== null){
            curKey = allkeys[i][0] + '[';
            Array.prototype.push.apply(
                allkeys,
                formatKeys( Object.entries(allkeys[i][1]) )
            );
        }
    return allkeys;
}
Run Code Online (Sandbox Code Playgroud)

或者,如果您只想要属性,而不想要具有属性的对象,那么您可以像这样过滤掉:

deepEntries(object).filter(function(x){return typeof x[1] !== 'object'});
Run Code Online (Sandbox Code Playgroud)

例子:

function stringifyEntries(allkeys){
    return allkeys.reduce(function(acc, x){
        return acc+((acc&&'\n')+x[0])
    }, '');
};
Run Code Online (Sandbox Code Playgroud)

浏览器兼容性

上述解决方案在 IE 中不起作用,而只能在 Edge 中起作用,因为它使用了 Object.entries 函数。如果您需要 IE9+ 支持,那么只需将以下Object.entriespolyfill添加到您的代码中。如果您出于某种原因,实际上确实需要 IE6+ 支持,那么您还需要一个Object.keysand JSON.stringifypolyfill(此处均未列出,因此请在其他地方找到)。

if (!Object.entries)
  Object.entries = function( obj ){
    var ownProps = Object.keys( obj ),
        i = ownProps.length,
        resArray = new Array(i); // preallocate the Array
    while (i--)
      resArray[i] = [ownProps[i], obj[ownProps[i]]];

    return resArray;
  };
Run Code Online (Sandbox Code Playgroud)