Dize yoluyla iç içe JavaScript nesnelerine ve aramalarına erişme


456

Ben böyle bir veri yapısı var:

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

Ve bu değişkeni kullanarak verilere erişmek istiyorum:

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

part1name, someObject.part1.name'Part 1' olan 'değeriyle doldurulmalıdır . 60 ile dolu part2quantity ile aynı şey.

Bunu saf javascript veya JQuery ile başarmak için yine de var mı?


Burada ne istediğinden emin değil misin? Part1.name sorgulamak ve "part1.name" metnini döndürmek istiyorsunuz? Veya part1.name içinde saklanan değeri almak için bir yol ister misiniz?
BonyT

Eğer böyle yapıyor çalıştı gelmiş var part1name = someObject.part1name;`
Rafay

1
@BonyT: someObject.part1.name dosyasını sorgulamak ve değerini döndürmek istiyorum ("Bölüm 1"). Ancak, bir sorgu 'part1name' depolanması (ben "anahtar" olarak adlandırılan) sorgu istiyorum. Cevabın için teşekkürler. @ 3nigma: Kesinlikle yaptım. Ama bu benim niyetim değil. Yanıtınız için teşekkürler.
Komaruloh

1
yinelenen cevapta, fyr'in cevabını seviyorum stackoverflow.com/questions/8817394/…
Steve Black

Yanıtlar:


520

Ben sadece zaten vardı bazı benzer kod dayalı yaptı, işe yarıyor gibi görünüyor:

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

Kullanımı ::

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

Http://jsfiddle.net/alnitak/hEsys/ adresinde çalışan bir demoya bakın

Bazıları DÜZENLE , en soldaki dizinlerin nesne içinde doğru bir şekilde yuvalanmış bir girdiye karşılık gelmediği bir dize iletilirse bu kodun bir hata atacağını fark etmişlerdir. Bu geçerli bir endişe kaynağıdır, ancak IMHO çağrılırken en iyi şekilde geçersiz bir dizin için try / catchbu işlevin sessiz bir şekilde dönmesini undefinedsağlamak yerine bir blokla ele alınır .


19
Bu güzel çalışıyor. Lütfen bunu bir düğüm paketi olarak sararak internete katkıda bulunun.
t3dodson

14
@ t3dodson Az önce yaptım: github.com/capaj/object-resolve-path sadece mülk adınız kendi içinde '[]' içerdiğinde bunun hoşlanmadığını unutmayın. Regex bunu '.' İle değiştirecektir. ve beklendiği gibi çalışmıyor
Capaj

20
harika şeyler; lodash kütüphanesini kullanarak, bir de şunları yapabilirsiniz:_.get(object, nestedPropertyString);
ian

17
Bu muhtemelen yorum denizinde kaybolacak, ancak var olmayan bir mülkü ele almaya çalıştığınızda hata veriyor. Yani 'part3[0].name.iDontExist'. oBir nesnenin olup olmadığını görmek için bir kontrol eklemek if insorunu çözer. (Bu konuda nasıl hareket edeceğiniz size kalmış). Güncellenmiş keman: jsfiddle.net/hEsys/418
ste2425

2
Bu çok altın. Bir yapılandırma tabanlı uygulama var ve bu biraz yararlı! Teşekkürler!
Christian Esperar

182

Kullandığım çözüm bu:

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

Örnek kullanım:

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

Sınırlamalar:

  • []Dizi indeksleri için köşeli parantez ( ) kullanılamaz; ancak ayırıcı jeton (örneğin .) arasında dizi indeksleri belirtmek yukarıda gösterildiği gibi iyi çalışır.

7
reduce kullanmak mükemmel bir çözümdür ( _.reduce()alt çizgi veya lodash kütüphanesinden de kullanılabilir )
Alp

3
Bence selfburada muhtemelen tanımsız. Bunu mu demek istediniz this?
Platinum Azure

2
İşte değerleri yola göre ayarlamak için benim tamamlayıcı: pastebin.com/jDp5sKT9
mroach

1
Bunu TypeScript'e nasıl bağlayacağınızı bilen var mı?
Adam Plocher

1
@ SC1000 iyi fikir. Bu yanıt, çoğu tarayıcıda varsayılan parametreler bulunmadan önce yazılmıştır. Genel nesneye varsayılan olarak başvurmak kasıtlı olduğundan, "işlev çözümlemesi (yol, obj = self)" olarak güncelleştireceğim.
speigg

180

Bu artık lodash kullanarak desteklenmektedir _.get(obj, property). Bkz. Https://lodash.com/docs#get

Dokümanlardan örnek:

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
Bu tek kabul edilen cevap olmalıdır, çünkü bu hem nokta hem de köşeli ayraç sözdizimi için çalışan tek yanıttır ve yoldaki bir anahtar dizesinde '[]' olduğunda başarısız olmaz.
Capaj

7
Bu. Artı, destekliyor_.set(...)
Josh

objet bulunmazsa ne olur?
17'de

@ Nesne olarak iletilen değer tanımsızsa veya bir nesne değilse _.get, sağlanan nesnede hiçbir anahtar bulunamadığında aynı davranışı gösterir. örneğin _.get(null, "foo") -> undefined, _.get(null, "foo", "bar") -> "bar". Ancak bu davranış dokümanda tanımlanmadığı için değişikliğe tabidir.
Ian Walker-Sperber

5
@Capaj seni öldürüyor musun? Ve kim kim istemez / kullanamaz?
Andre Figueiredo

74

ES6 : Vanila JS'de yalnızca bir satır (hata vermek yerine bulamazsa null değerini döndürür):

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

Veya örnek:

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

İsteğe bağlı zincirleme operatörü ile :

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

Yanlış, 0 ve negatif sayıyı da tanıyan ve varsayılan değerleri parametre olarak kabul eden kullanıma hazır bir işlev için:

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

Kullanılacak örnek:

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

Bonus :

To set (rob-Gordon @ tarafından talep) bir yol kullanabilirsiniz:

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

Misal:

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

[] İle erişim dizisi :

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

Misal:

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

2
Bu tekniği çok seviyorum. Bu gerçekten dağınık ama görev için bu tekniği kullanmak istedim. 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

1
Kullanma fikri gibi indirime, mantık kapalı görünüyor 0, undefinedve nulldeğerleri. yerine {a:{b:{c:0}}}döner . Belki de null veya undefined olup olmadığını açıkça denetlemek bu sorunları giderir. null0(p,c)=>p === undefined || p === null ? undefined : p[c]
SmujMaiku

Merhaba @SmujMaiku, 'kullanıma hazır' işlevi '0', 'tanımsız' ve 'null' için doğru bir şekilde dönüyor, ben sadece konsolda test ettim: resolPath ({a: {b: {c: 0}}}, ' abc ', null) => 0; Birden fazla kontrolden kaçınan anahtarın kendisinin yerine anahtarın mevcut olup olmadığını kontrol edin
Adriano Spadoni 16:17

Burada defaultValue işe yaramadı, Reflect.has(o, k) ? ...(ES6 Reflect.has ) kullanarak çalıştı
Andre Figueiredo

setPathdeğeri son seçicinin ilk tekrarında ayarlar. Örneğin let o = {}; setPath(o, 'a.a', 0), {a: 0}yerine sonuçları {a: {a: 0}}. bir çözüm için bu gönderiye bakınız .
1919'da pkfm

62

Dizeyi kendiniz ayrıştırmanız gerekir:

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';
    }
}

Bu, nokta gösterimiyle dizi dizinlerini de tanımlamanızı gerektirir:

var part3name1 = "part3.0.name";

Ayrıştırma işlemini kolaylaştırır.

DEMO


@Felix Kling: Çözümünüz bana ihtiyacım olanı sağlıyor. Bunun için çok teşekkür ederim. Ancak Alnitak da farklı yollar sunuyor ve aynı şekilde çalışıyor gibi görünüyor. Sadece bir cevap seçebileceğim için Alnitak cevabını seçeceğim. Onun çözümü sizden ya da bunun gibi bir şeyden daha iyi değil. Her neyse, verdiğiniz çözüm ve çabayı gerçekten takdir ediyorum.
Komaruloh

1
@Komaruloh: Oh Her zaman kendi sorunuza oy yanıtları verebileceğinizi düşündüm .... neyse az çok şaka yapıyordum, daha fazla itibara ihtiyacım yok;) Mutlu kodlama!
Felix Kling

1
@Felix Kling: Oy kullanabilmek için en az 15 itibara ihtiyacınız var. :) Ben 69k + ile daha fazla itibar gerekmez inanıyorum. Teşekkürler
Komaruloh

@Felix FWIW - []sözdiziminden özellik sözdizimine dönüştürmek oldukça önemsizdir.
Alnitak

4
While döngüsünü olarak değiştirirseniz, while (l > 0 && (obj = obj[current]) && i < l)bu kod nokta olmayan dizeler için de çalışır.
Snea

39

Nesnenin içindeki diziler / diziler için de çalışır. Geçersiz değerlere karşı savunma.

/**
 * 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
Teşekkürler bu en iyi ve en performanslı cevaptır - jsfiddle.net/Jw8XB/1
Dominic

@Ancak, yolun öğeleri noktalarla ayırması gerektiğini vurgulamak istiyorum. Diş telleri çalışmaz. Yani dizideki ilk öğeye erişmek için "0.sp ace" kullanın.
TheZver

26

eval kullanarak:

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

tanımlanmamış hatayı döndürmek için sarın

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

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

Değerlendirmenin gücünü kullanırken lütfen sağduyulu ve dikkatli olun. Biraz ışın kılıcı gibi, eğer açarsanız% 90 şansınız var. Herkes için değil.


7
Değerlendirmenin iyi bir fikir olup olmadığı, özellik dizesi verilerinin nereden geldiğine bağlıdır. Bilgisayar korsanlarının statik bir "var p = 'abc'; eval (p);" çağrı türü. Bunun için mükemmel bir fikir.
James Wilkins

17

Aşağıdaki hile ile herhangi bir harici JavaScript kütüphanesi olmadan nokta gösterimi ile derin bir nesne üyesinin değerini elde etmeyi başarabilirsiniz:

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

Senin durumunda değerini elde etmek için part1.namegelen someObjectsadece yapın:

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

İşte basit bir keman demosu: https://jsfiddle.net/harishanchu/oq5esowf/


3
işlev derin_değer (obj, yol) {yeni işlev döndür ('o', 'dönüş o.' + yol) (obj); }
ArcangelZith

14

Bu muhtemelen hiç gün ışığını görmeyecek ... ama yine de öyle.

  1. değiştirmek []Parantez sözdizimini şununla.
  2. Ayır .Karaktere
  3. Boş dizeleri kaldır
  4. Yolu bulun (aksi halde 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

Sersemleten tek bir astar.

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"

Ya da daha iyisi ...

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

Veya ES6 sürümü azaltılıyor ...

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

Plunkr


7

Bence bunu istiyorsun:

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

Bunu isteyebilirsiniz:

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

Her ikisi de çalışacak


Ya da belki bunu istiyorsun

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

var part1name = someObject[partName][nameStr];

Sonunda bunu isteyebilirsiniz

var partName = "part1.name";

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

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

Bence OP son çözümü istiyor. Ancak, dizelerin Splityöntemi yoktur, aksine split.
duri

Aslında sonuncuyu soruyordum. PartName değişkeni, değere anahtar yapısını belirten dize ile doldurulur. Çözümünüz mantıklı görünüyor. Ancak, verilerde 4-5 seviye ve daha fazlası gibi genişletilmiş derinlik için değişiklik yapmam gerekebilir. Ve ben dizi ve nesneye bununla tekdüze tedavi edip edemeyeceğini merak ediyorum?
Komaruloh

7

Burada birçok açıdan daha hızlı görünen daha fazla yol sunuyorum:

Seçenek 1: Dizeyi ayır. veya [veya] veya 'veya ", ters çevirin, boş öğeleri atlayın.

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

Seçenek 2 (en hızlısı hariç, hariç eval): Düşük seviye karakter taraması (normal ifade / bölme / vb. Yok, sadece hızlı karakter taraması). Not: Bu, dizinler için tırnak işaretlerini desteklemez.

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)

Seçenek 3: ( yeni : seçenek 2 tırnakları desteklemek için genişletildi - biraz daha yavaş, ancak yine de hızlı)

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 (...)" hala kral olsa da (performans akıllıca). Doğrudan kontrolünüz altında mülk yollarınız varsa, 'eval' kullanmayla ilgili herhangi bir sorun olmamalıdır (özellikle hız isteniyorsa). Özellik yollarını "telin üzerinden" ( hatta !? Lol: P) çekiyorsanız , evet, güvenli olmak için başka bir şey kullanın. Sadece bir aptal asla "eval" kullanmak için söyleyebilirim, çünkü bunu kullanmak için iyi nedenler vardır . Ayrıca, " Doug Crockford'un JSON ayrıştırıcısında kullanılır ." Giriş güvenliyse, sorun olmaz. Doğru iş için doğru aracı kullanın, hepsi bu.


6

Her ihtimale karşı, herkes en sonra 2017 veya bu soruyu ziyaret edip bir arayan kolay hatırlamak yolu, burada ayrıntılı bir blog yazısı var Erişim İç içe JavaScript nesneleri tarafından hortumlama olmadan

Undefined hatasının 'foo' özelliği okunamıyor

Dizi Azaltma Kullanarak İç İçe Nesnelere Erişme

Bu örnek yapıyı ele alalım

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

İç içe dizilere erişebilmek için kendi dizi azaltma util'inizi yazabilirsiniz.

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.

Ayrıca sizin için tüm bunları yapan mükemmel bir tür işleme minimal kütüphane türü vardır.

Typy ile kodunuz şöyle görünecek

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

Feragatname: Bu paketin yazarıyım.


6

angularjs

Speigg'in yaklaşımı çok düzgün ve temiz olsa da, bu cevabı AngularJS $ kapsam özelliklerine dize yolu ile erişme çözümünü ararken buldum ve küçük bir değişiklikle işi yapıyor:

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

Bu işlevi kök denetleyicinize yerleştirin ve aşağıdaki gibi herhangi bir alt kapsamı kullanın:

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

Bkz. AngularJS, AngularJS$scope.$eval ile bunu yapmanın başka bir yoluna sahiptir .
georgeawg

3

Dize yolu ile tüm işlemleri yapmak için henüz bir paket bulamadım, bu yüzden insert (), get () (varsayılan dönüş ile), set () ve remove () destekleyen kendi hızlı küçük paketimi yazdım ) operasyonlar.

Nokta gösterimi, köşeli ayraçlar, sayı indeksleri, dize numarası özellikleri ve sözcük olmayan karakterlere sahip tuşları kullanabilirsiniz. Aşağıda basit kullanım:

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

İle çalışır

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

Basit işlev, dize veya dizi yolu sağlar.

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

VEYA

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

Kodu yanıt olarak gönderecekseniz, lütfen kodun soruyu neden yanıtladığını açıklayın .
Tieson T.


2

Azaltmak iyi olsa da, hiç kimsenin kullanılmadığı için şaşırdım

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

Ölçek


Obj [key] öğesinin gerçekten var olup olmadığını bile kontrol etmiyorsunuz. Güvenilmez.
Carles Alcolea

@CarlesAlcolea varsayılan olarak js, bir nesnenin anahtarının var olup olmadığını kontrol a.b.cetmez b: nesnenizde özellik yoksa bir istisna oluşturur . Yanlış keys.forEach((key)=> obj = (obj||{})[key]);
tuş yolunu sessizce kapatan

Kıvırcık bir ayraç eksik bir nesne üzerinden koşuyorum, benim kötü :)
Carles Alcolea

1

Son zamanlarda aynı soruyu sordum ve nesne / dizileri de içeren https://npmjs.org/package/tea-properties'i başarıyla kullandım set:

almak:

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

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

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

Ayarlamak:

var o = {};

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

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

"Bu modül durduruldu. Chaijs / pathval kullan."
Patrick Fisher

1

@ Webjay'in cevabından ilham alan: https://stackoverflow.com/a/46008856/4110122

Nesnede herhangi bir değeri almak / ayarlamak / ayarlamak için kullanabileceğiniz bu işlevi yaptım

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

Kullanmak için:

 // 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 ile online mağaza geliştiriyorum. Gönderimde orijinal durumunu güncellemek için kopyalanan durum nesnesindeki değerleri değiştirmeye çalıştım. Yukarıdaki örnekler benim için çalışmadı, çünkü çoğu kopyalanan nesnenin yapısını değiştiriyor. Derin iç içe nesne özelliklerine erişim ve değerleri değiştirme işlevinin çalışma örneğini buldum: https://lowrey.me/create-an-object-by-path-in-javascript-2/ İşte:

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

Alnitak'ın cevabına dayanarak .

Çoklu dolguyu bir kontrole sardım ve işlevi tek zincirli bir azalmaya indirgedim.

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

Kodlama zamanında bilmeden farklı iç içe anahtarlara erişmeniz gerekiyorsa (bunlara hitap etmek önemsiz olacaktır) dizi gösterim erişimcisini kullanabilirsiniz:

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

Nokta gösterim erişimcisine eşdeğerdir ve çalışma zamanında değişebilir, örneğin:

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

var part1name = someObject[part][property];

eşittir

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

veya

var part1name = someObject.part1.name;

Umarım bu soruya hitap eder ...

DÜZENLE

Bir nesne değerine erişmek için bir tür xpath sorgusu korumak için bir dize kullanmayacağım . Sorguyu ayrıştırmak ve değeri almak için bir işlevi çağırmak zorunda gibi ben başka bir yol (değil:

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

veya uygula yönteminden rahatsızsanız

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

Fonksiyonlar daha kısa, daha açıktır, tercüman sözdizimi hataları vb. Sizin için kontrol eder.

Bu arada, doğru zamanda yapılan basit bir görevin yeterli olacağını hissediyorum ...


İlginç. Ancak benim durumumda, part1name'ye değer atadığımda someObject henüz başlatılıyor. Sadece yapıyı biliyorum. Bu yüzden yapıyı tanımlamak için dize kullanıyorum. Ve bunu someObject'ten verilerimi sorgulamak için kullanabilmeyi umuyorum. Düşüncelerinizi paylaştığınız için teşekkürler. :)
Komaruloh

@Komaruloh: Değişkenlerinizi oluştururken nesnenin henüz başlatılmadığını yazabilirsiniz. Bu arada neden anlamıyorum, ödevi neden uygun zamanda yapamıyorsun?
Eineki

SomeObject öğesinin henüz başlatılmadığından bahsetmediğim için üzgünüm. Nedeni gelince, someObject web servisi üzerinden getirilir. Ve ben part1name, part2qty, vb oluşur başlık bir dizi istiyorum. Böylece ben sadece header dizi döngü ve 'anahtar' / someObject yol olarak part1name değerine dayalı istediğim değeri elde edebilirsiniz.
Komaruloh

0

Buradaki çözümler sadece derin yuvalanmış anahtarlara erişmek içindir. Anahtarlara erişmek, eklemek, değiştirmek ve silmek için bir taneye ihtiyacım vardı. Ben geldi budur:

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: dizideki yol. İle değiştirebilirsiniz string_path.split(".").
  • type_of_function: Erişim için 0 (herhangi bir değeri geçme value), ekleme ve değiştirme için 0. 1 silmek için.

0

Alnitak'ın cevabının inşası:

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

}

Bu da bir değer ayarlamanızı sağlar!

Bir oluşturduk npm paketi ve GitHub bu yanı ile


0

Bir dize yerine, bir dizi iç içe nesnelerin ve dizilerin adreslenmesinde kullanılabilir, örneğin: ["my_field", "another_field", 0, "last_field", 10]

Aşağıda bu dizinin temsilini temel alan bir alan değişecektir. Yuvalanmış yapıların durumunu değiştiren kontrollü girdi alanları için reakt.js'de böyle bir şey kullanıyorum.

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

Önceki bir cevaba dayanarak, parantezleri de işleyebilen bir fonksiyon oluşturdum. Ama bölünme nedeniyle içlerinde nokta yok.

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

Çalışma Underscore'ler propertyveya 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>

İyi şanslar...

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.