ネストされたJavaScriptオブジェクトへのアクセスと文字列パスによる配列


454

私はこのようなデータ構造を持っています:

var someObject = {
    'part1' : {
        'name': 'Part 1',
        'size': '20',
        'qty' : '50'
    },
    'part2' : {
        'name': 'Part 2',
        'size': '15',
        'qty' : '60'
    },
    'part3' : [
        {
            'name': 'Part 3A',
            'size': '10',
            'qty' : '20'
        }, {
            'name': 'Part 3B',
            'size': '5',
            'qty' : '20'
        }, {
            'name': 'Part 3C',
            'size': '7.5',
            'qty' : '20'
        }
    ]
};

そして、私はこれらの変数を使用してデータにアクセスしたいと思います:

var part1name = "part1.name";
var part2quantity = "part2.qty";
var part3name1 = "part3[0].name";

part1nameにsomeObject.part1.nameは、「Part 1」というの値を入力する必要があります。60で満たされたpart2quantityと同じこと。

純粋なJavaScriptまたはJQueryのいずれかでこれを達成する方法はありますか?


ここで何を求めているのかわからない?part1.nameを照会して、「part1.name」というテキストを返せるようにしたいですか?または、part1.name内に格納されている値を取得する手段が必要ですか?
BonyT 2011年

あなたはvar part1name = someObject.part1name;`のようにやってみましたか
Rafay

1
@BonyT:someObject.part1.nameをクエリして、その値を返したい(「パート1」)。ただし、クエリ( "キー"と呼びます)を変数 'part1name'に格納します。お返事をありがとうございます。@ 3nigma:確かにそうだ。しかし、それは私の意図ではありません。返信いただきありがとうございます。
Komaruloh 2011年

1
重複した答えに、私はFYRの答えを愛するstackoverflow.com/questions/8817394/...
スティーブ・ブラック

回答:


520

私はすでに持っているいくつかの同様のコードに基づいてこれを作成しました、それは動作するようです:

Object.byString = function(o, s) {
    s = s.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties
    s = s.replace(/^\./, '');           // strip a leading dot
    var a = s.split('.');
    for (var i = 0, n = a.length; i < n; ++i) {
        var k = a[i];
        if (k in o) {
            o = o[k];
        } else {
            return;
        }
    }
    return o;
}

使用法::

Object.byString(someObj, 'part3[0].name');

http://jsfiddle.net/alnitak/hEsys/でデモをご覧ください

一部の編集者は、左端のインデックスがオブジェクト内の正しくネストされたエントリに対応していない文字列を渡すと、このコードがエラーをスローすることに気づきました。これは有効な懸念事項ですが、IMHOはtry / catch呼び出し時にブロックを使用して対処することをお勧めしundefinedます。この関数に無効なインデックスを通知することなく通知するのではありません。


19
これは美しく機能します。これをノードパッケージとしてラップしてインターネットに貢献してください。
t3dodson 2015年

14
@ t3dodson私がやっただけ:github.com/capaj/object-resolve-pathプロパティ名に '[]'が含まれている場合、これはうまく機能しないことに注意してください。正規表現は「。」に置き換えます。そして、それは期待通りに機能しません
Capaj 2015

20
素晴らしいもの; lodashライブラリを使用して、1にも行うことができます_.get(object, nestedPropertyString);
イアン・

17
これはコメントの海で失われる可能性がありますが、存在しないプロパティに対処しようとするとエラーになります。ですから'part3[0].name.iDontExist'。問題oif in修正されたオブジェクトであるかどうかを確認するチェックを追加します。(それをどうするかはあなた次第です)。更新されたフィドルを参照してください:jsfiddle.net/hEsys/418
ste2425

2
これはとてもゴールドです。設定ベースのアプリケーションがあり、これはちょっと便利です!ありがとう!
クリスチャンエスペラー

182

これは私が使用するソリューションです:

function resolve(path, obj=self, separator='.') {
    var properties = Array.isArray(path) ? path : path.split(separator)
    return properties.reduce((prev, curr) => prev && prev[curr], obj)
}

使用例:

// accessing property path on global scope
resolve("document.body.style.width")
// or
resolve("style.width", document.body)

// accessing array indexes
// (someObject has been defined in the question)
resolve("part3.0.size", someObject) // returns '10'

// accessing non-existent properties
// returns undefined when intermediate properties are not defined:
resolve('properties.that.do.not.exist', {hello:'world'})

// accessing properties with unusual keys by changing the separator
var obj = { object: { 'a.property.name.with.periods': 42 } }
resolve('object->a.property.name.with.periods', obj, '->') // returns 42

// accessing properties with unusual keys by passing a property name array
resolve(['object', 'a.property.name.with.periods'], obj) // returns 42

制限:

  • []配列インデックスに角かっこ()を使用することはできません。ただし、.上記のように、セパレータートークンの間に配列インデックスを指定すると(たとえば、)正常に機能します。

7
reduceの使用は優れたソリューションです(_.reduce()アンダースコアまたはlodashライブラリからも使用できます)
Alp

3
selfここではおそらく未定義だと思います。もしかしてthis
Platinum Azure 2014年

2
ここでは、パスによって設定された値に私の補完だ:pastebin.com/jDp5sKT9
mroach

1
これをTypeScriptに移植する方法を知っている人はいますか?
Adam Plocher 2017

1
@ SC1000良いアイデア。この回答は、ほとんどのブラウザーでデフォルトのパラメーターが利用可能になる前に書かれました。グローバル関数をデフォルトとして参照することは意図的なものであるため、「function resolve(path、obj = self)」に更新します。
speigg 2018

179

これは、を使用したlodashでサポートされるようになりました_.get(obj, property)https://lodash.com/docs#getを参照してください

ドキュメントの例:

var object = { 'a': [{ 'b': { 'c': 3 } }] };

_.get(object, 'a[0].b.c');
// → 3

_.get(object, ['a', '0', 'b', 'c']);
// → 3

_.get(object, 'a.b.c', 'default');
// → 'default'

9
ドットとブラケットの両方の構文で機能するのはこれだけであり、パスのキーの文字列に「[]」が含まれていても失敗しないため、これが唯一の受け入れられる答えになるはずです。
Capaj 2015

7
この。プラス、それはサポートしています_.set(...)
ジョシュ・C.

オブジェクトが見つからない場合はどうなりますか?
DDave 2017年

@DDaveは、オブジェクトとして渡された値が未定義またはオブジェクトでない場合_.get、提供されたオブジェクトにキーが見つからない場合と同じ動作を示します。例えば_.get(null, "foo") -> undefined_.get(null, "foo", "bar") -> "bar"。ただし、この動作はドキュメントで定義されていないため、変更される可能性があります。
Ian Walker-Sperber 2017年

5
@Capaj you kiddin '?そして、誰がlodashを使いたくない/使用できないのですか?
Andre Figueiredo

74

ES6:Vanila JSの1行のみ(エラーが発生するのではなく、見つからない場合はnullを返します):

'path.string'.split('.').reduce((p,c)=>p&&p[c]||null, MyOBJ)

または例:

'a.b.c'.split('.').reduce((p,c)=>p&&p[c]||null, {a:{b:{c:1}}})

オプションの連鎖演算子を使用する場合

'a.b.c'.split('.').reduce((p,c)=>p?.[c]||null, {a:{b:{c:1}}})

false、0、負の数も認識し、デフォルト値をパラメーターとして受け入れる、すぐに使用できる関数の場合:

const resolvePath = (object, path, defaultValue) => path
   .split('.')
   .reduce((o, p) => o ? o[p] : defaultValue, object)

使用例:

resolvePath(window,'document.body') => <body>
resolvePath(window,'document.body.xyz') => undefined
resolvePath(window,'document.body.xyz', null) => null
resolvePath(window,'document.body.xyz', 1) => 1

ボーナス

パスを設定するには(@ rob-gordonによって要求されます)、次を使用できます。

const setPath = (object, path, value) => path
   .split('.')
   .reduce((o,p,i) => o[p] = path.split('.').length === ++i ? value : o[p] || {}, object)

例:

let myVar = {}
setPath(myVar, 'a.b.c', 42) => 42
console.log(myVar) => {a: {b: {c: 42}}}

[]で配列にアクセス

const resolvePath = (object, path, defaultValue) => path
   .split(/[\.\[\]\'\"]/)
   .filter(p => p)
   .reduce((o, p) => o ? o[p] : defaultValue, object)

例:

const myVar = {a:{b:[{c:1}]}}
resolvePath(myVar,'a.b[0].c') => 1
resolvePath(myVar,'a["b"][\'0\'].c') => 1

2
私はこのテクニックが大好きです。これは本当に厄介ですが、このテクニックを割り当てに使用したかったのです。let o = {a:{b:{c:1}}}; let str = 'a.b.c'; str.split('.').splice(0, str.split('.').length - 1).reduce((p,c)=>p&&p[c]||null, o)[str.split('.').slice(-1)] = "some new value";
rob-gordon 2017年

1
reduceを使用するというアイデアは好きですが、あなたのロジックは0undefinedおよびのnull値に対してオフになっているようです。 {a:{b:{c:0}}}null代わりに戻ります0。おそらく明示的にnullまたはundefinedをチェックすることで、これらの問題を解決できます。 (p,c)=>p === undefined || p === null ? undefined : p[c]
SmujMaiku 2017年

こんにちは、@ SmujMaikuです。「使用可能」関数は「0」、「未定義」、「null」に対して正しく戻ります。コンソールでテストしたところです。resolvePath({a:{b:{c:0}}}、 ' abc '、null)=> 0; 複数のチェックを回避するために、値自体ではなくキーが存在するかどうかをチェックします
Adriano Spadoni

ここではdefaultValueが機能せず、Reflect.has(o, k) ? ...(ES6 Reflect.has)を使用しても機能しました
Andre Figueiredo

setPath最後のセレクタが最初に出現したときに値を設定します。たとえばlet o = {}; setPath(o, 'a.a', 0){a: 0}ではなくになり{a: {a: 0}}ます。解決策については、この投稿を参照してください。
pkfm

62

文字列を自分で解析する必要があります。

function getProperty(obj, prop) {
    var parts = prop.split('.');

    if (Array.isArray(parts)) {
        var last = parts.pop(),
        l = parts.length,
        i = 1,
        current = parts[0];

        while((obj = obj[current]) && i < l) {
            current = parts[i];
            i++;
        }

        if(obj) {
            return obj[last];
        }
    } else {
        throw 'parts is not valid array';
    }
}

これには、ドット表記を使用して配列インデックスも定義する必要がありました。

var part3name1 = "part3.0.name";

解析が簡単になります。

デモ


@Felix Kling:あなたのソリューションは私に必要なものを提供してくれます。そして私はそれをたくさんありがとう。しかし、Alnitakはさまざまな方法も提供しており、どちらでも機能するようです。答えは1つしか選べないので、アルニタクの答えを選びます。彼の解決策があなたやそのようなものよりも優れているわけではありません。とにかく、私はあなたの解決策と努力に本当に感謝しています。
Komaruloh 2011年

1
@Komaruloh:ああ、あなたはいつでも自分の質問に回答を賛成投票できると思っていました...とにかく私は多かれ少なかれ冗談でした、私はもっと評判を必要としません;)ハッピーコーディング!
Felix Kling、2011年

1
@Felix Kling:賛成票を投じるには、少なくとも15の評判が必要です。:)私はあなたが69k +でこれ以上の評判を必要としないと信じています。ありがとう
Komaruloh

@Felix FWIW- []構文からプロパティ構文への変換は非常に簡単です。
Alnitak

4
whileループをに変更するとwhile (l > 0 && (obj = obj[current]) && i < l)、このコードはドットのない文字列でも機能します。
14

39

オブジェクト内の配列/配列でも機能します。無効な値に対する防御。

/**
 * Retrieve nested item from object/array
 * @param {Object|Array} obj
 * @param {String} path dot separated
 * @param {*} def default value ( if result undefined )
 * @returns {*}
 */
function path(obj, path, def){
    var i, len;

    for(i = 0,path = path.split('.'), len = path.length; i < len; i++){
        if(!obj || typeof obj !== 'object') return def;
        obj = obj[path[i]];
    }

    if(obj === undefined) return def;
    return obj;
}

//////////////////////////
//         TEST         //
//////////////////////////

var arr = [true, {'sp ace': true}, true]

var obj = {
  'sp ace': true,
  arr: arr,
  nested: {'dotted.str.ing': true},
  arr3: arr
}

shouldThrow(`path(obj, "arr.0")`);
shouldBeDefined(`path(obj, "arr[0]")`);
shouldBeEqualToNumber(`path(obj, "arr.length")`, 3);
shouldBeTrue(`path(obj, "sp ace")`);
shouldBeEqualToString(`path(obj, "none.existed.prop", "fallback")`, "fallback");
shouldBeTrue(`path(obj, "nested['dotted.str.ing'])`);
<script src="https://cdn.rawgit.com/coderek/e7b30bac7634a50ad8fd/raw/174b6634c8f57aa8aac0716c5b7b2a7098e03584/js-test.js"></script>


10
ありがとう、これは最高で最もパフォーマンスの高い回答です-jsfiddle.net/Jw8XB/1
Dominic

@Endless、パスでアイテムをドットで区切る必要があることを強調したいと思います。ブレースは機能しません。つまり、配列の最初の項目にアクセスするには、「0.sp ace」を使用します。
TheZver 2016年

26

evalを使用:

var part1name = eval("someObject.part1.name");

エラー時に未定義を返すためのラップ

function path(obj, path) {
    try {
        return eval("obj." + path);
    } catch(e) {
        return undefined;
    }
}

http://jsfiddle.net/shanimal/b3xTw/

evalの機能を使用する際は、常識と注意を払ってください。ライトサーベルのようなものです。オンにすると、90%の確率で手足が切断されます。それは皆のためではありません。


7
evalが適切かどうかは、プロパティ文字列データがどこから来ているかによって異なります。静的な "var p = 'abc'; eval(p);"を介してハッカーが侵入することに懸念を抱く理由はないと思います。タイプコール。それは完全に素晴らしい考えです。
James Wilkins

17

次の簡単なトリックで、外部JavaScriptライブラリなしでドット表記でディープオブジェクトメンバーの値を取得できます。

new Function('_', 'return _.' + path)(obj);

あなたの場合ではpart1.nameからの値を取得するsomeObjectだけです:

new Function('_', 'return _.part1.name')(someObject);

ここに簡単なフィドルのデモがあります:https : //jsfiddle.net/harishanchu/oq5esowf/


3
function deep_value(obj、path){return new Function( 'o'、 'return o。' + path)(obj); }
ArcangelZith

14

これはおそらく日の目を見ることはないでしょう...しかし、ここはとにかくそれです。

  1. []ブラケット構文を次のものに置き換えます.
  2. .文字で分割
  3. 空の文字列を削除する
  4. パスを見つける(それ以外の場合undefined

// "one liner" (ES6)

const deep_value = (obj, path) => 
  path
    .replace(/\[|\]\.?/g, '.')
    .split('.')
    .filter(s => s)
    .reduce((acc, val) => acc && acc[val], obj);
    
// ... and that's it.

var someObject = {
    'part1' : {
        'name': 'Part 1',
        'size': '20',
        'qty' : '50'
    },
    'part2' : {
        'name': 'Part 2',
        'size': '15',
        'qty' : '60'
    },
    'part3' : [
        {
            'name': 'Part 3A',
            'size': '10',
            'qty' : '20'
        }
        // ...
    ]
};

console.log(deep_value(someObject, "part1.name"));               // Part 1
console.log(deep_value(someObject, "part2.qty"));                // 60
console.log(deep_value(someObject, "part3[0].name"));            // Part 3A


11

ロダッシュ付きワンライナーです。

const deep = { l1: { l2: { l3: "Hello" } } };
const prop = "l1.l2.l3";
const val = _.reduce(prop.split('.'), function(result, value) { return result ? result[value] : undefined; }, deep);
// val === "Hello"

またはさらに良い...

const val = _.get(deep, prop);

または削減を伴うES6バージョン...

const val = prop.split('.').reduce((r, val) => { return r ? r[val] : undefined; }, deep);

プランカー


7

私はあなたがこれを求めていると思います:

var part1name = someObject.part1.name;
var part2quantity = someObject.part2.qty;
var part3name1 =  someObject.part3[0].name;

あなたはこれを求めている可能性があります:

var part1name = someObject["part1"]["name"];
var part2quantity = someObject["part2"]["qty"];
var part3name1 =  someObject["part3"][0]["name"];

どちらも機能します


または多分あなたはこれを求めています

var partName = "part1";
var nameStr = "name";

var part1name = someObject[partName][nameStr];

最後に、あなたはこれを求めている可能性があります

var partName = "part1.name";

var partBits = partName.split(".");

var part1name = someObject[partBits[0]][partBits[1]];

OPは最後の解決策を求めていると思います。ただし、文字列にはSplitメソッドがなく、splitです。
duri

実は最後の方にお願いしてました。partName変数には、値へのキー構造を示す文字列が入ります。あなたの解決策は理にかなっているようです。ただし、4〜5レベルなど、データの拡張深度を変更する必要がある場合があります。そして、これで配列とオブジェクトを均一に処理できるかどうか疑問に思っていますか?
Komaruloh 2011年

7

ここで私は多くの点でより速く見えるより多くの方法を提供します:

オプション1:で文字列を分割します。または[または]または 'または "、それを逆にして、空の項目をスキップします。

function getValue(path, origin) {
    if (origin === void 0 || origin === null) origin = self ? self : this;
    if (typeof path !== 'string') path = '' + path;
    var parts = path.split(/\[|\]|\.|'|"/g).reverse(), name; // (why reverse? because it's usually faster to pop off the end of an array)
    while (parts.length) { name=parts.pop(); if (name) origin=origin[name]; }
    return origin;
}

オプション2(を除くすべての中で最速eval):低レベルの文字スキャン(正規表現/分割などなし、ただの文字スキャン)。 注:これはインデックスの引用符をサポートしていません。

function getValue(path, origin) {
    if (origin === void 0 || origin === null) origin = self ? self : this;
    if (typeof path !== 'string') path = '' + path;
    var c = '', pc, i = 0, n = path.length, name = '';
    if (n) while (i<=n) ((c = path[i++]) == '.' || c == '[' || c == ']' || c == void 0) ? (name?(origin = origin[name], name = ''):(pc=='.'||pc=='['||pc==']'&&c==']'?i=n+2:void 0),pc=c) : name += c;
    if (i==n+2) throw "Invalid path: "+path;
    return origin;
} // (around 1,000,000+/- ops/sec)

オプション3: 新しい:サポート引用符に拡張オプション2 -少し遅く、それでも速いです)

function getValue(path, origin) {
    if (origin === void 0 || origin === null) origin = self ? self : this;
    if (typeof path !== 'string') path = '' + path;
    var c, pc, i = 0, n = path.length, name = '', q;
    while (i<=n)
        ((c = path[i++]) == '.' || c == '[' || c == ']' || c == "'" || c == '"' || c == void 0) ? (c==q&&path[i]==']'?q='':q?name+=c:name?(origin?origin=origin[name]:i=n+2,name='') : (pc=='['&&(c=='"'||c=="'")?q=c:pc=='.'||pc=='['||pc==']'&&c==']'||pc=='"'||pc=="'"?i=n+2:void 0), pc=c) : name += c;
    if (i==n+2 || name) throw "Invalid path: "+path;
    return origin;
}

JSPerf:http ://jsperf.com/ways-to-dereference-a-delimited-property-string/3

「eval(...)」は依然として重要です(つまり、パフォーマンスの点で)。コントロールの直下にプロパティパスがある場合、「eval」の使用に問題はありません(特に速度が必要な場合)。「回線を介して」(ライン上で!?lol:Pで)プロパティパスをプルする場合、はい、安全のために他のものを使用します。「eval」を使用することには十分な理由があるため、「eval」をまったく使用しないと言うのは馬鹿だけです。また、「Doug CrockfordのJSONパーサーで使用されています。」入力が安全であれば、まったく問題ありません。適切な仕事に適切なツールを使用する、それだけです。


6

念のため、誰のは、2017年以降にこの質問を訪問し、探しやすく、覚えている方法は、ここに精巧なブログの投稿のアクセスネストされたが、JavaScriptでオブジェクトによって酔っぱらっされることなく、

未定義のプロパティ 'foo'を読み取れませんエラーの

Array Reduceを使用してネストされたオブジェクトにアクセスする

この例の構造を見てみましょう

const user = {
    id: 101,
    email: 'jack@dev.com',
    personalInfo: {
        name: 'Jack',
        address: [{
            line1: 'westwish st',
            line2: 'washmasher',
            city: 'wallas',
            state: 'WX'
        }]
    }
}

ネストされた配列にアクセスできるようにするには、独自の配列reduce utilを記述できます。

const getNestedObject = (nestedObj, pathArr) => {
    return pathArr.reduce((obj, key) =>
        (obj && obj[key] !== 'undefined') ? obj[key] : undefined, nestedObj);
}

// pass in your object structure as array elements
const name = getNestedObject(user, ['personalInfo', 'name']);

// to access nested array, just pass in array index as an element the path array.
const city = getNestedObject(user, ['personalInfo', 'address', 0, 'city']);
// this will return the city from the first address item.

最小限のライブラリーの取り扱い優れたタイプもありますtypyは、あなたのためのすべてのこれを行います。

typyを使用すると、コードは次のようになります。

const city = t(user, 'personalInfo.address[0].city').safeObject;

免責事項:私はこのパッケージの作成者です。


6

AngularJS

Speiggのアプローチは非常にきちんとしていてクリーンですが、AngularJS $ scopeプロパティに文字列パスでアクセスするソリューションを検索しているときにこの返信を見つけ、少し変更してそれを行いました:

$scope.resolve = function( path, obj ) {
    return path.split('.').reduce( function( prev, curr ) {
        return prev[curr];
    }, obj || this );
}

この関数をルートコントローラーに配置して、次のような子スコープで使用します。

$scope.resolve( 'path.to.any.object.in.scope')

AngularJSで$scope.$evalそれを行う別の方法についてAngularJSを参照してください。
georgeawg

3

文字列パスを使用してすべての操作を実行するパッケージをまだ見つけていないので、insert()、get()(デフォルトのreturn)、set()、およびremove()をサポートする独自の簡単な小さなパッケージを作成しました) オペレーション。

ドット表記、角かっこ、数字のインデックス、文字列の数字のプロパティ、単語以外の文字を含むキーを使用できます。以下の簡単な使用法:

> var jsocrud = require('jsocrud');

...

// Get (Read) ---
> var obj = {
>     foo: [
>         {
>             'key w/ non-word chars': 'bar'
>         }
>     ]
> };
undefined

> jsocrud.get(obj, '.foo[0]["key w/ non-word chars"]');
'bar'

https://www.npmjs.com/package/jsocrud

https://github.com/vertical-knowledge/jsocrud


3
/**
 * Access a deep value inside a object 
 * Works by passing a path like "foo.bar", also works with nested arrays like "foo[0][1].baz"
 * @author Victor B. https://gist.github.com/victornpb/4c7882c1b9d36292308e
 * Unit tests: http://jsfiddle.net/Victornpb/0u1qygrh/
 */
function getDeepVal(obj, path) {
    if (typeof obj === "undefined" || obj === null) return;
    path = path.split(/[\.\[\]\"\']{1,2}/);
    for (var i = 0, l = path.length; i < l; i++) {
        if (path[i] === "") continue;
        obj = obj[path[i]];
        if (typeof obj === "undefined" || obj === null) return;
    }
    return obj;
}

で動作します

getDeepVal(obj,'foo.bar')
getDeepVal(obj,'foo.1.bar')
getDeepVal(obj,'foo[0].baz')
getDeepVal(obj,'foo[1][2]')
getDeepVal(obj,"foo['bar'].baz")
getDeepVal(obj,"foo['bar']['baz']")
getDeepVal(obj,"foo.bar.0.baz[1]['2']['w'].aaa[\"f\"].bb")

3

文字列または配列パスを許可する単純な関数。

function get(obj, path) {
  if(typeof path === 'string') path = path.split('.');

  if(path.length === 0) return obj;
  return get(obj[path[0]], path.slice(1));
}

const obj = {a: {b: {c: 'foo'}}};

console.log(get(obj, 'a.b.c')); //foo

または

console.log(get(obj, ['a', 'b', 'c'])); //foo

コードを回答として投稿する場合は、コードが質問に回答する理由を説明てください。
Tieson T.


2

reduceは良いですが、それぞれに使用されていないことに驚きます:

function valueForKeyPath(obj, path){
        const keys = path.split('.');
        keys.forEach((key)=> obj = obj[key]);
        return obj;
    };

テスト


obj [key]が実際に存在するかどうかさえチェックしていません。それは信頼できません。
Carles Alcolea

@CarlesAlcoleaのデフォルトでは、jsはオブジェクトのキーが存在するかどうかもチェックしa.b.cませんb。オブジェクトにプロパティがない場合、例外を発生させます。誤って間違ったキーパスを無視するものが必要な場合(これはお勧めしません)、forEachをこれに置き換えることができますkeys.forEach((key)=> obj = (obj||{})[key]);
Flavien Volken

中かっこが欠けていたオブジェクトを実行しましたが、私の悪い:)
Carles Alcolea

1

最近同じ質問があり、ネストされたオブジェクト/配列であるhttps://npmjs.org/package/tea-propertiesを正常に使用しましたset

取得する:

var o = {
  prop: {
    arr: [
      {foo: 'bar'}
    ]
  }
};

var properties = require('tea-properties');
var value = properties.get(o, 'prop.arr[0].foo');

assert(value, 'bar'); // true

セットする:

var o = {};

var properties = require('tea-properties');
properties.set(o, 'prop.arr[0].foo', 'bar');

assert(o.prop.arr[0].foo, 'bar'); // true

「このモジュールは廃止されました。chaijs/ pathvalを使用してください。」
Patrick Fisher

1

@webjayの答えに触発されました:https//stackoverflow.com/a/46008856/4110122

オブジェクト内の任意の値を取得/設定/設定解除するために使用できるこの関数を作成しました

function Object_Manager(obj, Path, value, Action) 
{
    try
    {
        if(Array.isArray(Path) == false)
        {
            Path = [Path];
        }

        let level = 0;
        var Return_Value;
        Path.reduce((a, b)=>{
            level++;
            if (level === Path.length)
            {
                if(Action === 'Set')
                {
                    a[b] = value;
                    return value;
                }
                else if(Action === 'Get')
                {
                    Return_Value = a[b];
                }
                else if(Action === 'Unset')
                {
                    delete a[b];
                }
            } 
            else 
            {
                return a[b];
            }
        }, obj);
        return Return_Value;
    }

    catch(err)
    {
        console.error(err);
        return obj;
    }
}

それを使用するには:

 // Set
 Object_Manager(Obj,[Level1,Level2,Level3],New_Value, 'Set');

 // Get
 Object_Manager(Obj,[Level1,Level2,Level3],'', 'Get');

 // Unset
 Object_Manager(Obj,[Level1,Level2,Level3],'', 'Unset');

1

Reactを使用してオンラインショップを開発しています。コピーされた状態オブジェクトの値を変更して、送信時に元の状態に更新しようとしました。上記の例は、コピーされたオブジェクトの構造が変化するため、うまくいきませんでした。深くネストされたオブジェクトのプロパティの値にアクセスして変更するための関数の実用的な例を見つけました:https : //lowrey.me/create-an-object-by-path-in-javascript-2/ここにあります:

const createPath = (obj, path, value = null) => {
  path = typeof path === 'string' ? path.split('.') : path;
  let current = obj;
  while (path.length > 1) {
    const [head, ...tail] = path;
    path = tail;
    if (current[head] === undefined) {
      current[head] = {};
    }
    current = current[head];
  }
  current[path[0]] = value;
  return obj;
};

1

アルニタクの答えに基づく

私はポリフィルをチェックで包み、機能を単一の連鎖削減に減らしました。

if (Object.byPath === undefined) {
  Object.byPath = (obj, path) => path
    .replace(/\[(\w+)\]/g, '.$1')
    .replace(/^\./, '')
    .split(/\./g)
    .reduce((ref, key) => key in ref ? ref[key] : ref, obj)
}

const data = {
  foo: {
    bar: [{
      baz: 1
    }]
  }
}

console.log(Object.byPath(data, 'foo.bar[0].baz'))


0

コーディング時にそれを知らずに異なるネストされたキーにアクセスする必要がある場合(それらに対処することは簡単です)、配列表記のアクセサーを使用できます。

var part1name = someObject['part1']['name'];
var part2quantity = someObject['part2']['qty'];
var part3name1 =  someObject['part3'][0]['name'];

これらはドット表記アクセサと同等であり、実行時に変化する可能性があります。次に例を示します。

var part = 'part1';
var property = 'name';

var part1name = someObject[part][property];

に相当

var part1name = someObject['part1']['name'];

または

var part1name = someObject.part1.name;

これがあなたの質問に答えることを願っています...

編集

文字列を使用して、オブジェクト値にアクセスするための一種のxpath クエリを作成しません。クエリを解析して値を取得するために関数を呼び出す必要があるので、別のパスをたどります(ではありません:

var part1name = function(){ return this.part1.name; }
var part2quantity = function() { return this['part2']['qty']; }
var part3name1 =  function() { return this.part3[0]['name'];}

// usage: part1name.apply(someObject);

または、申請方法に不安がある場合

var part1name = function(obj){ return obj.part1.name; }
var part2quantity = function(obj) { return obj['part2']['qty']; }
var part3name1 =  function(obj) { return obj.part3[0]['name'];}

// usage: part1name(someObject);

関数はより短く、より明確であり、インタープリターは構文エラーなどについてそれらをチェックします。

ちなみに、適時の簡単な割り当てで十分だと思います...


面白い。しかし、私の場合、part1nameに値を割り当てたときにsomeObjectはまだ初期化されています。私は構造だけを知っています。そのため、構造を説明するために文字列を使用しています。そして、それを使用してsomeObjectから私のデータをクエリできることを期待しています。ご意見をお寄せいただきありがとうございます。:)
Komaruloh '27 / 06/27

@Komaruloh:変数を作成するとき、オブジェクトはまだ初期化されていないと書いていると思います。ところで、ポイントが取れないのですが、適切なタイミングで課題を出せないのはなぜですか?
Eineki

someObjectがまだ初期化されていないことについて言及しなかったことについて申し訳ありません。その理由は、someObjectがWebサービス経由でフェッチされているためです。そして、part1name、part2qtyなどで構成されるヘッダーの配列が欲しいので、ヘッダー配列をループして、part1nameの値に基づいて必要な値をsomeObjectへの「キー」/パスとして取得できます。
Komaruloh 2011年

0

ここでのソリューションは、深くネストされたキーにアクセスするためのものです。キーのアクセス、追加、変更、削除のために必要でした。これは私が思いついたものです:

var deepAccessObject = function(object, path_to_key, type_of_function, value){
    switch(type_of_function){
        //Add key/modify key
        case 0: 
            if(path_to_key.length === 1){
                if(value)
                    object[path_to_key[0]] = value;
                return object[path_to_key[0]];
            }else{
                if(object[path_to_key[0]])
                    return deepAccessObject(object[path_to_key[0]], path_to_key.slice(1), type_of_function, value);
                else
                    object[path_to_key[0]] = {};
            }
            break;
        //delete key
        case 1:
            if(path_to_key.length === 1){
                delete object[path_to_key[0]];
                return true;
            }else{
                if(object[path_to_key[0]])
                    return deepAccessObject(object[path_to_key[0]], path_to_key.slice(1), type_of_function, value);
                else
                    return false;
            }
            break;
        default:
            console.log("Wrong type of function");
    }
};
  • path_to_key:配列内のパス。で置き換えることができますstring_path.split(".")
  • type_of_function:アクセスの場合は0(値をに渡さないvalue)、追加および変更の場合は0。削除の場合は1。

0

アルニタックの答えを基に構築:

if(!Object.prototype.byString){
  //NEW byString which can update values
Object.prototype.byString = function(s, v, o) {
  var _o = o || this;
      s = s.replace(/\[(\w+)\]/g, '.$1'); // CONVERT INDEXES TO PROPERTIES
      s = s.replace(/^\./, ''); // STRIP A LEADING DOT
      var a = s.split('.'); //ARRAY OF STRINGS SPLIT BY '.'
      for (var i = 0; i < a.length; ++i) {//LOOP OVER ARRAY OF STRINGS
          var k = a[i];
          if (k in _o) {//LOOP THROUGH OBJECT KEYS
              if(_o.hasOwnProperty(k)){//USE ONLY KEYS WE CREATED
                if(v !== undefined){//IF WE HAVE A NEW VALUE PARAM
                  if(i === a.length -1){//IF IT'S THE LAST IN THE ARRAY
                    _o[k] = v;
                  }
                }
                _o = _o[k];//NO NEW VALUE SO JUST RETURN THE CURRENT VALUE
              }
          } else {
              return;
          }
      }
      return _o;
  };

}

これにより、値を設定することもできます!

私もこれでnpmパッケージgithubを作成しました


0

文字列の代わりに、配列をネストされたオブジェクトや配列に使用できます。例: ["my_field", "another_field", 0, "last_field", 10]

この配列表現に基づいてフィールドを変更する例を次に示します。ネストされた構造の状態を変更する制御された入力フィールドに対して、react.jsでそのようなものを使用しています。

let state = {
        test: "test_value",
        nested: {
            level1: "level1 value"
        },
        arr: [1, 2, 3],
        nested_arr: {
            arr: ["buh", "bah", "foo"]
        }
    }

function handleChange(value, fields) {
    let update_field = state;
    for(var i = 0; i < fields.length - 1; i++){
        update_field = update_field[fields[i]];
    }
    update_field[fields[fields.length-1]] = value;
}

handleChange("update", ["test"]);
handleChange("update_nested", ["nested","level1"]);
handleChange(100, ["arr",0]);
handleChange('changed_foo', ["nested_arr", "arr", 3]);
console.log(state);

0

以前の回答に基づいて、角かっこも処理できる関数を作成しました。しかし、分割のためにそれらの中にドットはありません。

function get(obj, str) {
  return str.split(/\.|\[/g).map(function(crumb) {
    return crumb.replace(/\]$/, '').trim().replace(/^(["'])((?:(?!\1)[^\\]|\\.)*?)\1$/, (match, quote, str) => str.replace(/\\(\\)?/g, "$1"));
  }).reduce(function(obj, prop) {
    return obj ? obj[prop] : undefined;
  }, obj);
}

0

// (IE9+) Two steps

var pathString = "[0]['property'].others[3].next['final']";
var obj = [{
  property: {
    others: [1, 2, 3, {
      next: {
        final: "SUCCESS"
      }
    }]
  }
}];

// Turn string to path array
var pathArray = pathString
    .replace(/\[["']?([\w]+)["']?\]/g,".$1")
    .split(".")
    .splice(1);

// Add object prototype method
Object.prototype.path = function (path) {
  try {
    return [this].concat(path).reduce(function (f, l) {
      return f[l];
    });
  } catch (e) {
    console.error(e);
  }
};

// usage
console.log(obj.path(pathArray));
console.log(obj.path([0,"doesNotExist"]));


0

Underscorepropertyまたはでの作業propertyOf

var test = {
  foo: {
    bar: {
      baz: 'hello'
    }
  }
}
var string = 'foo.bar.baz';


// document.write(_.propertyOf(test)(string.split('.')))

document.write(_.property(string.split('.'))(test));
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.9.1/underscore-min.js"></script>

幸運を...

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.