68

状況: 複数のサブおよびサブサブ オブジェクトを含む大きなオブジェクトがあり、複数のデータ型を含むプロパティがあります。この目的のために、このオブジェクトは次のようになります。

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

このオブジェクトをループして、階層を示すキーのリストを作成する必要があるため、リストは次のようになります。

aProperty.aSetting1
aProperty.aSetting2
aProperty.aSetting3
aProperty.aSetting4
aProperty.aSetting5
bProperty.bSetting1.bPropertySubSetting
bProperty.bSetting2
cProperty.cSetting

オブジェクトをループしてキーを吐き出しますが、階層的にではありません。

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

誰かがこれを行う方法を教えてもらえますか? これはあなたがいじるためのjsfiddleです:http://jsfiddle.net/tbynA/

4

17 に答える 17

117

私はあなたのためにフィドルを作りました。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, '')

更新 (2019 年 1 月 17 日) - <以前は別の実装がありましたが、機能しませんでした。より良い解決策については、この回答を参照してください:)>

于 2013-03-28T19:47:05.490 に答える
63

Artyom Neustroev のソリューションは複雑なオブジェクトでは機能しないため、彼のアイデアに基づいた有効なソリューションを次に示します。

function propertiesToArray(obj) {
  const isObject = val =>
    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);
}
  
const foo = {foo: {bar: {baz: undefined}, fub: 'goz', bag: {zar: {zaz: null}, raz: 3}}}
const result = propertiesToArray(foo)
console.log(result)

于 2018-12-04T20:25:55.620 に答える
18

オブジェクトのオブジェクト グラフにループがある場合、次のような問題が発生します。

var object = {
    aProperty: {
        aSetting1: 1
    },
};
object.ref = object;

その場合、すでに通過したオブジェクトの参照を保持し、反復から除外したい場合があります。

また、オブジェクト グラフが次のように深すぎる場合、問題が発生する可能性があります。

var object = {
  a: { b: { c: { ... }} }
};

再帰呼び出しが多すぎるというエラーが発生します。どちらも回避できます。

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); 
于 2016-02-11T15:01:28.470 に答える
14

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
// }

後でインデックスを取得するためにループするだけです。

于 2016-04-17T17:29:21.093 に答える
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;
}

次に、探している結果を出力するには、これを使用します。

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

技術的なビットに興味がある場合は、これがどのように機能するかです。Object.entries渡したオブジェクトの を取得し、objそれらを array に入れることで機能しallkeysます。次に、 の最初からallkeys最後まで、allkeysエントリ値の 1 つがオブジェクトであることがわかった場合、そのエントリのキーを として取得し、その結果の配列を の最後にプッシュする前にcurKey、独自の各エントリ キーの前に を付けます。次に、追加されたエントリの数をターゲットの長さに追加して、新しく追加されたキーも処理するようにします。curKeyallkeysallkeys

たとえば、次の点に注意してください。

<script>
var object = {
    aProperty: {
        aSetting1: 1,
        aSetting2: 2,
        aSetting3: 3,
        aSetting4: 4,
        aSetting5: 5
    },
    bProperty: {
        bSetting1: {
            bPropertySubSetting : true
        },
        bSetting2: "bString"
    },
    cProperty: {
        cSetting: "cString"
    }
}
document.write(
    '<pre>' + stringifyEntries( deepEntries(object) ) + '</pre>'
);
function deepEntries( obj ){//debugger;
    '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;
}
function stringifyEntries(allkeys){
    return allkeys.reduce(function(acc, x){
        return acc+((acc&&'\n')+x[0])
    }, '');
};
</script>

または、プロパティを持つオブジェクトではなく、プロパティのみが必要な場合は、次のようにフィルター処理して除外できます。

deepEntries(object).filter(function(x){return typeof x[1] !== 'object'});

例:

<script>
var object = {
    aProperty: {
        aSetting1: 1,
        aSetting2: 2,
        aSetting3: 3,
        aSetting4: 4,
        aSetting5: 5
    },
    bProperty: {
        bSetting1: {
            bPropertySubSetting : true
        },
        bSetting2: "bString"
    },
    cProperty: {
        cSetting: "cString"
    }
}
document.write('<pre>' + stringifyEntries(
    deepEntries(object).filter(function(x){
       return typeof x[1] !== 'object';
    })
) + '</pre>');
function deepEntries( obj ){//debugger;
    '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;
}
function stringifyEntries(allkeys){
    return allkeys.reduce(function(acc, x){
        return acc+((acc&&'\n')+x[0])
    }, '');
};
</script>

ブラウザの互換性

上記のソリューションは IE では機能しません。Object.entries 関数を使用しているため、Edge でのみ機能します。IE9+ のサポートが必要な場合は、次のObject.entriesポリフィルをコードに追加するだけです。私以外の何らかの理由で、実際に IE6+ のサポートが必要な場合は、Object.keysandJSON.stringifyポリフィルも必要になります (どちらもここにはリストされていないため、別の場所で見つけてください)。

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;
  };
于 2017-08-12T01:30:46.577 に答える
1

フィルタリングの可能性を備えた改善されたソリューション。この結果は、次のような配列パスを使用して任意のオブジェクト プロパティを直接参照できるため、より便利です。

["aProperty.aSetting1"、"aProperty.aSetting2"、"aProperty.aSetting3"、"aProperty.aSetting4"、"aProperty.aSetting5"、"bProperty.bSetting1.bPropertySubSetting"、"bProperty.bSetting2"、"cProperty.cSetting" ]

 /**
 * Recursively searches for properties in a given object. 
 * Ignores possible prototype endless enclosures. 
 * Can list either all properties or filtered by key name.
 *
 * @param {Object} object Object with properties.
 * @param {String} key Property key name to search for. Empty string to 
 *                     get all properties list .
 * @returns {String} Paths to properties from object root.
 */
function getPropertiesByKey(object, key) {

  var paths = [
  ];

  iterate(
    object,
    "");

  return paths;

  /**
   * Single object iteration. Accumulates to an outer 'paths' array.
   */
  function iterate(object, path) {
    var chainedPath;

    for (var property in object) {
      if (object.hasOwnProperty(property)) {

        chainedPath =
          path.length > 0 ?
          path + "." + property :
          path + property;

        if (typeof object[property] == "object") {

          iterate(
            object[property],
            chainedPath,
            chainedPath);
        } else if (
          property === key ||
          key.length === 0) {

          paths.push(
            chainedPath);
        }
      }
    }

    return paths;
  }
}
于 2016-06-06T13:46:41.807 に答える
1

これが簡単な解決策です。これは遅い答えですが、単純なものかもしれません-

const data = {
  city: 'foo',
  year: 2020,
  person: {
    name: {
      firstName: 'john',
      lastName: 'doe'
    },
    age: 20,
    type: {
      a: 2,
      b: 3,
      c: {
        d: 4,
        e: 5
      }
    }
  },
}

function getKey(obj, res = [], parent = '') {
  const keys = Object.keys(obj);
  
  /** Loop throw the object keys and check if there is any object there */
  keys.forEach(key => {
    if (typeof obj[key] !== 'object') {
      // Generate the heirarchy
      parent ? res.push(`${parent}.${key}`) : res.push(key);
    } else {
      // If object found then recursively call the function with updpated parent
      let newParent = parent ? `${parent}.${key}` : key;
      getKey(obj[key], res, newParent);
    }
    
  });
}

const result = [];

getKey(data, result, '');

console.log(result);
.as-console-wrapper{min-height: 100%!important; top: 0}

于 2020-12-17T16:49:06.913 に答える
1

次のような JSON オブジェクトがあるとします。

var example = {
    "prop1": "value1",
    "prop2": [ "value2_0", "value2_1"],
    "prop3": {
         "prop3_1": "value3_1"
    }
}

その「プロパティ」を反復処理する間違った方法:

function recursivelyIterateProperties(jsonObject) {
    for (var prop in Object.keys(jsonObject)) {
        console.log(prop);
        recursivelyIterateProperties(jsonObject[prop]);
    }
}

とおよびのプロパティを繰り返し処理しているとき0に、 などのコンソール ログを見て驚くかもしれません。これらのオブジェクトはシーケンスであり、シーケンスのインデックスは Javascript のそのオブジェクトのプロパティです。1prop1prop2prop3_1

JSON オブジェクトのプロパティを再帰的に反復するより良い方法は、そのオブジェクトがシーケンスかどうかを最初に確認することです。

function recursivelyIterateProperties(jsonObject) {
    for (var prop in Object.keys(jsonObject)) {
        console.log(prop);
        if (!(typeof(jsonObject[prop]) === 'string')
            && !(jsonObject[prop] instanceof Array)) {
                recursivelyIterateProperties(jsonObject[prop]);

            }
     }
}

arrays 内のオブジェクト内のプロパティを検索する場合は、次の手順を実行します。

function recursivelyIterateProperties(jsonObject) {

    if (jsonObject instanceof Array) {
        for (var i = 0; i < jsonObject.length; ++i) {
            recursivelyIterateProperties(jsonObject[i])
        }
    }
    else if (typeof(jsonObject) === 'object') {
        for (var prop in Object.keys(jsonObject)) {
            console.log(prop);
            if (!(typeof(jsonObject[prop]) === 'string')) {
                recursivelyIterateProperties(jsonObject[prop]);
            }
        }
    }
}
于 2016-01-28T21:15:09.523 に答える