Son not: Bu cevabın pek çok oy aldığından memnun olduğum halde, biraz dehşete kapıldım. Eğer birinin "xabc" gibi nokta gösterim dizelerini referanslara dönüştürmesi gerekiyorsa, (belki de) çok yanlış bir şeylerin olduğuna dair bir işaret olabilir (belki garip bir serileştirme yapmıyorsanız).
Yani, bu cevaba giden yolu bulan acemiler kendilerine "bunu neden yapıyorum?"
Kullanım durumunuz küçükse ve performans sorunlarıyla karşılaşmayacaksanız, bunu yapmak genellikle iyidir ve daha sonra daha karmaşık hale getirmek için soyutlamanıza dayanmanız gerekmez. Aslında, bu kod karmaşıklığını azaltacak ve işleri basit tutacaksa, muhtemelen devam etmeli ve OP'nin istediği şeyi yapmalısınız. Ancak, durum böyle değilse, bunlardan herhangi birinin geçerli olup olmadığını düşünün:
durum 1 : Verilerinizle çalışmanın birincil yöntemi olarak (örneğin, uygulamanızın nesnelerin etrafından aktarılması ve bunların kaydının silinmesi gibi). "Bir dizeden bir işlevi veya değişken adını nasıl arayabilirim" diye sormak gibi.
- Bu kötü bir programlama uygulamasıdır (özellikle gereksiz metaprogramlama ve işlev yan etkisi olmayan kodlama stilini ihlal eder ve performans isabetlerine sahip olacaktır). Bu durumda kendilerini bulan acemiler bunun yerine dizi gösterimleriyle, örneğin ['x', 'a', 'b', 'c'] veya mümkünse daha doğrudan / basit / anlaşılır bir şeyle çalışmayı düşünmelidir: kaybetmemek gibi referansların kendileri ilk etapta izler (sadece istemci tarafı veya sadece sunucu tarafı ise en idealdir), vb. (Önceden var olan benzersiz bir kimlik eklemek yetersiz kalır, ancak spesifikasyon gerektiriyorsa kullanılabilir ne olursa olsun var.)
durum 2 : Serileştirilmiş verilerle veya kullanıcıya görüntülenecek verilerle çalışma. Bir Date nesnesi yerine bir tarihi "1999-12-30" dizesi olarak kullanmak gibi (dikkatli değilse saat dilimi hatalarına veya ek serileştirme karmaşıklığına neden olabilir). Ya da ne yaptığını biliyorsun.
- Bu belki iyi. Nokta dizesi olmadığına dikkat edin. sterilize edilmiş giriş parçalarınızda.
Bu yanıtı her zaman kullanırken ve dize ile dizi arasında geçiş yaparken bulursanız, kötü durumda olabilirsiniz ve bir alternatif düşünmelisiniz.
İşte diğer çözümlerden 10 kat daha kısa olan zarif bir astar:
function index(obj,i) {return obj[i]}
'a.b.etc'.split('.').reduce(index, obj)
[değiştir] Veya ECMAScript 6'da:
'a.b.etc'.split('.').reduce((o,i)=>o[i], obj)
(Ben başkalarının önerdiği gibi eval her zaman kötü olduğunu düşünüyorum değil (genellikle olsa da), yine de bu insanlar bu yöntemin eval kullanmadığı için memnun olacak. Yukarıdaki obj.a.b.etc
verilen obj
ve dize bulacaksınız "a.b.etc"
.)
reduce
ECMA-262 standardında (5. baskı) olmasına rağmen hala kullanmaktan korkanlara yanıt olarak , işte iki satırlı yinelemeli bir uygulama:
function multiIndex(obj,is) { // obj,['1','2','3'] -> ((obj['1'])['2'])['3']
return is.length ? multiIndex(obj[is[0]],is.slice(1)) : obj
}
function pathIndex(obj,is) { // obj,'1.2.3' -> multiIndex(obj,['1','2','3'])
return multiIndex(obj,is.split('.'))
}
pathIndex('a.b.etc')
JS derleyicisinin yaptığı optimizasyonlara bağlı olarak, iç içe işlevlerin her çağrıda olağan yöntemler (bir kapatma, nesne veya genel ad alanına yerleştirme) yoluyla yeniden tanımlanmadığından emin olmak isteyebilirsiniz.
düzenleme :
Yorumlardaki ilginç bir soruyu cevaplamak için:
bunu nasıl bir pasör haline getirirdin? Değerleri yalnızca yoldan döndürmekle kalmaz, aynı zamanda işleve yeni bir değer gönderilirse bunları ayarlar mı? - Swader 28 Haziran, 21:42
(sidenote: ne yazık ki bir Setter ile bir nesneyi geri döndüremez, çünkü bu çağırma kuralını ihlal eder; yorumcu bunun yerine index(obj,"a.b.etc", value)
yapmak gibi yan etkileri olan genel bir setter tarzı fonksiyona atıfta bulunuyor gibi görünüyor obj.a.b.etc = value
.)
reduce
Tarzı buna çok da uygun değil ama özyinelemeli uygulama değiştirebilirsiniz:
function index(obj,is, value) {
if (typeof is == 'string')
return index(obj,is.split('.'), value);
else if (is.length==1 && value!==undefined)
return obj[is[0]] = value;
else if (is.length==0)
return obj;
else
return index(obj[is[0]],is.slice(1), value);
}
Demo:
> obj = {a:{b:{etc:5}}}
> index(obj,'a.b.etc')
5
> index(obj,['a','b','etc']) #works with both strings and lists
5
> index(obj,'a.b.etc', 123) #setter-mode - third argument (possibly poor form)
123
> index(obj,'a.b.etc')
123
... kişisel olarak ayrı bir fonksiyon yapmayı tavsiye ederim setIndex(...)
. Sorunun orijinal pozerinin .split
dizelerden ziyade indeks dizileri ile çalışabileceğini (olmalı mı?) Bir yan notla bitirmek istiyorum ; ancak genellikle bir kullanışlılık işlevinde yanlış bir şey yoktur.
Bir yorumcu sordu:
diziler ne olacak? "ab [4] .cd [1] [2] [3]" gibi bir şey mi? -AlexS
Javascript çok garip bir dildir; Genelde nesnelerin özellik anahtarları olarak yalnızca dizeleri olabilir, bu nedenle örneğin x
genel bir nesne olsaydı x={}
, ... doğru okudunuz ... evet ...x[1]
x["1"]
Javascript Dizileri (kendileri Nesne örneğidir), benzer bir şey yapabilseniz bile, tamsayı anahtarlarını özellikle teşvik eder x=[]; x["puppy"]=5;
.
Ancak genel olarak (ve istisnalar vardır), x["somestring"]===x.somestring
(izin verildiğinde; yapamazsınız x.123
).
(Kullandığınız JS derleyicisinin, spesifikasyonu ihlal etmeyeceğini kanıtlayabiliyorsa, bunları daha akılcı temsillere derlemeyi seçebileceğini unutmayın.)
Bu nedenle, sorunuzun yanıtı, bu nesnelerin yalnızca tamsayıları kabul edip etmediğinize (sorun etki alanınızdaki bir kısıtlama nedeniyle) bağlı olup olmadığınıza bağlıdır. Varsayalım. Bu durumda geçerli bir ifade, temel tanımlayıcının artı bazı .identifier
s artı bazı ["stringindex"]
s
a["b"][4]["c"]["d"][1][2][3]
Muhtemelen de desteklememiz gerekse de, bu buna eşdeğer olacaktır a.b["c\"validjsstringliteral"][3]
. Geçerli bir dize değişmezinin nasıl ayrıştırılacağını görmek için dize değişmezleri üzerindeki ecmascript dilbilgisi bölümünü denetlemeniz gerekir . Teknik a
olarak, geçerli bir javascript tanımlayıcısı olan (ilk cevabımın aksine) de kontrol etmek istersiniz .
Sorunuza basit cevabı olsa da dizeleri virgül veya parantez içeren yoksa , sadece değil kümesindeki karakterlerin uzunluğu 1+ dizilerini maç için olmak olacak ,
ya [
ya ]
:
> "abc[4].c.def[1][2][\"gh\"]".match(/[^\]\[.]+/g)
// ^^^ ^ ^ ^^^ ^ ^ ^^^^^
["abc", "4", "c", "def", "1", "2", ""gh""]
Dizeleriniz kaçış karakterleri veya "
karakterler içermiyorsa ve IdentifierNames, StringLiterals'in bir alt dili olduğundan (bence ???) önce noktalarınızı [] biçimine dönüştürebilirsiniz:
> var R=[], demoString="abc[4].c.def[1][2][\"gh\"]";
> for(var match,matcher=/^([^\.\[]+)|\.([^\.\[]+)|\["([^"]+)"\]|\[(\d+)\]/g;
match=matcher.exec(demoString); ) {
R.push(Array.from(match).slice(1).filter(x=>x!==undefined)[0]);
// extremely bad code because js regexes are weird, don't use this
}
> R
["abc", "4", "c", "def", "1", "2", "gh"]
Tabii ki, daima dikkatli olun ve asla verilerinize güvenmeyin. Bazı kullanım durumlarında işe yarayabilecek bazı kötü yollar da şunları içerir:
// hackish/wrongish; preprocess your string into "a.b.4.c.d.1.2.3", e.g.:
> yourstring.replace(/]/g,"").replace(/\[/g,".").split(".")
"a.b.4.c.d.1.2.3" //use code from before
Özel 2018 düzenlemesi:
Tam daire gidelim ve sözdizimsel saflık hamfistery yararına ... ile gelebilir en verimsiz, korkunç overmetaprogrammed çözüm yapalım. ES6 Proxy nesneleri ile! ... (imho iyi ve harika ama) yanlış yazılmış kütüphaneleri bozabilecek bazı özellikleri de tanımlayalım. Performansı, akıl sağlığını (sizin veya başkalarının), işinizi vb. Önemsiyorsanız, bunu kullanmaya dikkat etmelisiniz.
// [1,2,3][-1]==3 (or just use .slice(-1)[0])
if (![1][-1])
Object.defineProperty(Array.prototype, -1, {get() {return this[this.length-1]}}); //credit to caub
// WARNING: THIS XTREME™ RADICAL METHOD IS VERY INEFFICIENT,
// ESPECIALLY IF INDEXING INTO MULTIPLE OBJECTS,
// because you are constantly creating wrapper objects on-the-fly and,
// even worse, going through Proxy i.e. runtime ~reflection, which prevents
// compiler optimization
// Proxy handler to override obj[*]/obj.* and obj[*]=...
var hyperIndexProxyHandler = {
get: function(obj,key, proxy) {
return key.split('.').reduce((o,i)=>o[i], obj);
},
set: function(obj,key,value, proxy) {
var keys = key.split('.');
var beforeLast = keys.slice(0,-1).reduce((o,i)=>o[i], obj);
beforeLast[keys[-1]] = value;
},
has: function(obj,key) {
//etc
}
};
function hyperIndexOf(target) {
return new Proxy(target, hyperIndexProxyHandler);
}
Demo:
var obj = {a:{b:{c:1, d:2}}};
console.log("obj is:", JSON.stringify(obj));
var objHyper = hyperIndexOf(obj);
console.log("(proxy override get) objHyper['a.b.c'] is:", objHyper['a.b.c']);
objHyper['a.b.c'] = 3;
console.log("(proxy override set) objHyper['a.b.c']=3, now obj is:", JSON.stringify(obj));
console.log("(behind the scenes) objHyper is:", objHyper);
if (!({}).H)
Object.defineProperties(Object.prototype, {
H: {
get: function() {
return hyperIndexOf(this); // TODO:cache as a non-enumerable property for efficiency?
}
}
});
console.log("(shortcut) obj.H['a.b.c']=4");
obj.H['a.b.c'] = 4;
console.log("(shortcut) obj.H['a.b.c'] is obj['a']['b']['c'] is", obj.H['a.b.c']);
Çıktı:
obj: {"a": {"b": {"c": 1, "d": 2}}}
(proxy geçersiz kılma get) objHyper ['abc']: 1
(proxy geçersiz kılma kümesi) objHyper ['abc'] = 3, şimdi obj: {"a": {"b": {"c": 3, "d": 2}}}
(sahne arkasında) objHyper: Proxy {a: {…}}
(kısayol) obj.H ['abc'] = 4
[kısayol) obj.H ['abc'] obj ['a'] ['b'] ['c']: 4
verimsiz fikir: Girdi bağımsız değişkenini temel alarak gönderilecekleri değiştirebilirsiniz; ya .match(/[^\]\[.]+/g)
yöntemi desteklemek için kullanın obj['keys'].like[3]['this']
veya eğer instanceof Array
bir Array'ı girdi gibi kabul edin keys = ['a','b','c']; obj.H[keys]
.
Belki de tanımsız indeksleri 'daha yumuşak' bir NaN-tarzı bir şekilde ele almak istediğiniz öneri başına (örneğin index({a:{b:{c:...}}}, 'a.x.c')
yakalanmamış TypeError yerine tanımsız olarak geri dön) ...:
1) Bu, 1 boyutlu dizin durumunda "bir hata atmak yerine tanımsız dönmeliyiz" perspektifinden anlamlıdır ({}) ['örneğin'] == tanımsız, bu nedenle " hatası "hatası.
2) Bu , yukarıdaki örnekte bir TypeError hatasıyla başarısız olacağımız, yaptığımız perspektiften anlamlı değildirx['a']['x']['c']
.
Bununla birlikte, azaltma işlevinizi aşağıdakilerden biriyle değiştirerek bu işi yapacaksınız:
(o,i)=>o===undefined?undefined:o[i]
, veya
(o,i)=>(o||{})[i]
.
(Bir for döngüsü kullanarak ve bir sonraki dizine eklediğiniz alt sonuç tanımsız olduğunda kırma / döndürme veya bu tür hataların yeterince nadir olmasını bekliyorsanız bir try-catch kullanarak bunu daha verimli hale getirebilirsiniz.)
eval
kötüdür; kullanmayın