Biorąc pod uwagę obiekt JS
var obj = { a: { b: '1', c: '2' } }
i ciąg
"a.b"
jak mogę przekonwertować ciąg na notację kropkową, aby móc przejść
var val = obj.a.b
Gdyby ciąg był tylko 'a'
, mógłbym użyć obj[a]
. Ale to jest bardziej złożone. Wyobrażam sobie, że istnieje jakaś prosta metoda, ale obecnie ucieka.
javascript
nevf
źródło
źródło
eval
jest zły; nie używaj tegoOdpowiedzi:
Oto elegancki jednowarstwowy, który jest 10 razy krótszy niż inne rozwiązania:
[edytuj] Lub w ECMAScript 6:
(Nie to, że myślę, że eval zawsze jest zły, jak sugerują inni (choć zwykle tak jest), ale ci ludzie będą zadowoleni, że ta metoda nie używa eval. Powyższe znajdzie
obj.a.b.etc
daneobj
i ciąg"a.b.etc"
.)W odpowiedzi na tych, którzy nadal boją się używać,
reduce
mimo że są w standardzie ECMA-262 (wydanie piąte), oto dwutorowa rekurencyjna implementacja:W zależności od optymalizacji wykonywanych przez kompilator JS, możesz chcieć upewnić się, że żadne zagnieżdżone funkcje nie zostaną ponownie zdefiniowane przy każdym wywołaniu zwykłymi metodami (umieszczając je w zamknięciu, obiekcie lub globalnej przestrzeni nazw).
edycja :
Aby odpowiedzieć na interesujące pytanie w komentarzach:
(sidenote: niestety nie może zwrócić obiektu za pomocą Settera, ponieważ naruszałoby to konwencję wywoływania; komentator wydaje się raczej odwoływać do ogólnej funkcji w stylu Setera z efektami ubocznymi, takimi jak
index(obj,"a.b.etc", value)
robienieobj.a.b.etc = value
.)reduce
Styl jest naprawdę nie nadaje się do tego, ale możemy zmodyfikować rekurencyjną realizacji:Próbny:
... choć osobiście poleciłbym osobną funkcję
setIndex(...)
. Chciałbym zakończyć na marginesie, że oryginalny poser pytania mógł (powinien?) Pracować z tablicami indeksów (z których mogą uzyskać.split
), a nie z łańcuchami; chociaż zwykle nie ma nic złego w funkcji wygody.Komentator zapytał:
JavaScript jest bardzo dziwnym językiem; ogólnie rzecz biorąc, obiekty mogą mieć tylko łańcuchy jako klucze właściwości, więc na przykład, jeśli byłby to
x
obiekt ogólnyx={}
, tox[1]
stałby sięx["1"]
... czytasz to dobrze ... tak ...Tablice JavaScript (które same są instancjami Object) szczególnie zachęcają do wprowadzania liczb całkowitych, nawet jeśli można coś takiego zrobić
x=[]; x["puppy"]=5;
.Ale ogólnie (i są wyjątki),
x["somestring"]===x.somestring
(gdy jest to dozwolone; nie można tego zrobićx.123
).(Należy pamiętać, że dowolny kompilator JS, którego używasz, może wybrać kompilację tych danych w celu uzyskania lepszej reprezentacji, jeśli może udowodnić, że nie naruszy specyfikacji).
Tak więc odpowiedź na twoje pytanie zależy od tego, czy zakładasz, że te obiekty akceptują tylko liczby całkowite (z powodu ograniczenia w Twojej problematycznej dziedzinie), czy nie. Załóżmy, że nie. Zatem poprawnym wyrażeniem jest konkatenacja identyfikatora podstawowego plus niektóre
.identifier
s plus niektóre["stringindex"]
sByłoby to wtedy równoważne
a["b"][4]["c"]["d"][1][2][3]
, choć prawdopodobnie powinniśmy również wspieraća.b["c\"validjsstringliteral"][3]
. Musisz sprawdzić sekcję gramatyki ekmascript na literałach łańcuchów, aby zobaczyć, jak parsować poprawne literały łańcuchowe. Technicznie chciałbyś również sprawdzić (inaczej niż w mojej pierwszej odpowiedzi), czya
jest to prawidłowy identyfikator javascript .Prosta odpowiedź na pytanie jednak, czy struny nie zawierają przecinków lub nawiasów , byłoby po prostu dopasować długość 1+ sekwencje znaków nie w zestawie
,
lub[
lub]
:Jeśli twoje ciągi nie zawierają znaków specjalnych ani
"
znaków , a ponieważ IdentifierNames są podjęzykiem StringLiterals (myślę, że?), Możesz najpierw przekonwertować swoje kropki na []:Oczywiście zawsze bądź ostrożny i nigdy nie ufaj swoim danym. Niektóre złe sposoby na zrobienie tego, które mogą działać w niektórych przypadkach użycia, obejmują również:
Edycja specjalna 2018:
Okrążmy koło i przygotujmy najbardziej nieefektywne, okropnie zaprogramowane rozwiązanie, jakie możemy wymyślić ... w interesie syntaktycznej
czystości. Z obiektami ES6 Proxy! ... Zdefiniujmy również niektóre właściwości, które (imho są w porządku i cudowne, ale) mogą uszkodzić niepoprawnie napisane biblioteki. Być może powinieneś być ostrożny z korzystaniem z tego, jeśli zależy Ci na wydajności, zdrowiu psychicznym (swoim lub innych), pracy itp.Próbny:
Wynik:
nieefektywny pomysł: Możesz zmodyfikować powyższe do wysyłki w oparciu o argument wejściowy; albo użyj tej
.match(/[^\]\[.]+/g)
metody do obsługiobj['keys'].like[3]['this']
, albo jeśliinstanceof Array
, a następnie po prostu zaakceptuj tablicę jako dane wejściowe, takie jakkeys = ['a','b','c']; obj.H[keys]
.Zgodnie z sugestią, że być może chcesz obsługiwać niezdefiniowane wskaźniki w „bardziej miękki” sposób w stylu NaN (np.
index({a:{b:{c:...}}}, 'a.x.c')
Zwracaj niezdefiniowane, a nie wyłapane TypeError) ...:1) Ma to sens z punktu widzenia „powinniśmy zwrócić niezdefiniowany zamiast rzucać błąd” w sytuacji indeksu jednowymiarowego ({}) [„np.”] == niezdefiniowany, więc „powinniśmy zwrócić niezdefiniowany zamiast rzucać błąd ”w sytuacji N-wymiarowej.
2) Nie ma to sensu z perspektywy, którą robimy
x['a']['x']['c']
, co zawiódłoby w przypadku błędu TypeError w powyższym przykładzie.Powiedziałbyś, że sprawisz, że to zadziała, zastępując swoją funkcję redukującą:
(o,i)=>o===undefined?undefined:o[i]
Lub(o,i)=>(o||{})[i]
.(Możesz to uczynić bardziej wydajnym, używając pętli for i przerywania / zwracania za każdym razem, gdy wynik, w którym będziesz indeksować dalej, jest niezdefiniowany lub używając try-catch, jeśli spodziewacie się, że takie niepowodzenia będą wystarczająco rzadkie.)
źródło
reduce
nie jest obsługiwany we wszystkich obecnie używanych przeglądarkach.Array.reduce
jest częścią standardu ECMA-262. Jeśli naprawdę chcesz obsługiwać przestarzałe przeglądarki, możesz zdefiniowaćArray.prototype.reduce
przykładową implementację podaną gdzieś (np. Developer.mozilla.org/en/JavaScript/Reference/Global_Objects/… ).var setget = function( obj, path ){ function index( robj,i ) {return robj[i]}; return path.split('.').reduce( index, obj ); }
Jeśli możesz użyć lodash , istnieje funkcja, która robi dokładnie to:
_.get (obiekt, ścieżka, [defaultValue])
źródło
_.get(object, path)
nie psuje się, jeśli ścieżka nie została znaleziona.'a.b.etc'.split('.').reduce((o,i)=>o[i], obj)
robi. W moim konkretnym przypadku - nie w każdym przypadku - dokładnie tego, czego potrzebowałem. Dzięki!defaultValue
. Że_.get()
metoda zwraca wartość domyślną, jeśli_.get()
postanawiaundefined
, więc ustawić go na cokolwiek chcesz i zegarek do wartości ustawionej._.set(object, path, value)
.Nieco bardziej zaangażowany przykład z rekurencją.
źródło
możesz także użyć lodash.get
Wystarczy zainstalować ten pakiet (npm i --save lodash.get), a następnie użyć go w następujący sposób:
źródło
Jeśli spodziewasz się wyłuskać tę samą ścieżkę wiele razy, zbudowanie funkcji dla każdej ścieżki notacji kropkowej ma jak dotąd najlepszą wydajność (rozwinięcie testów perfów, do których James Wilkins przywołał w komentarzach powyżej).
Korzystanie z konstruktora funkcji ma te same wady co eval () pod względem bezpieczeństwa i wydajności w najgorszym przypadku, ale IMO jest bardzo niewykorzystanym narzędziem w przypadkach, gdy potrzebujesz połączenia ekstremalnej dynamiki i wysokiej wydajności. Używam tej metodologii do budowania funkcji filtrów tablicowych i wywoływania ich w pętli AngularJS. Moje profile konsekwentnie pokazują krok array.filter (), biorąc dereferencję w czasie krótszym niż 1ms i filtrują około 2000 złożonych obiektów, używając dynamicznie zdefiniowanych ścieżek o głębokości 3-4 poziomów.
Podobną metodologię można oczywiście zastosować do tworzenia funkcji ustawiających:
źródło
Wiele lat od oryginalnego postu. Teraz jest świetna biblioteka o nazwie „ścieżka obiektu”. https://github.com/mariocasciaro/object-path
Dostępne w NPM i BOWER https://www.npmjs.com/package/object-path
To tak proste jak:
Działa w przypadku głęboko zagnieżdżonych właściwości i tablic.
źródło
Proponuję podzielić ścieżkę, powtórzyć ją i zmniejszyć posiadany obiekt. Ta propozycja działa z wartością domyślną dla brakujących właściwości.
źródło
Uwaga: jeśli już korzystasz z Lodash , możesz użyć funkcji
property
lubget
:Znak podkreślenia ma również
property
funkcję, ale nie obsługuje notacji kropkowej.źródło
Inne propozycje są trochę tajemnicze, więc pomyślałem, że wrócę:
źródło
Możesz skorzystać z biblioteki dostępnej w npm, co upraszcza ten proces. https://www.npmjs.com/package/dot-object
źródło
Jest to dużo kodu w porównaniu do znacznie prostszego
eval
sposobu robienia tego, ale jak mówi Simon Willison, nigdy nie powinieneś używać eval .Również JSFiddle .
źródło
Rozszerzyłem elegancką odpowiedź przez ninjagecko, aby funkcja obsługiwała zarówno odwołania do stylu kropkowanego, jak i tablicowego oraz aby pusty ciąg spowodował zwrócenie obiektu nadrzędnego.
Proszę bardzo:
Zobacz mój działający przykład jsFiddle tutaj: http://jsfiddle.net/sc0ttyd/q7zyd/
źródło
[]
zapis jest zawsze dla tablic. w ten sposób mogą być reprezentowane klucze obiektów, na przykładobj['some-problem/name'].list[1]
Aby to naprawić, musiałem zaktualizowaćarr_deref
taką funkcjęjavascript function arr_deref(o, ref, i) { return !ref ? o : (o[(ref.slice(0, i ? -1 : ref.length)).replace(/^['"]|['"]$/g, '')]); }
źródło
Wartość elementu członkowskiego można uzyskać za pomocą notacji kropkowej za pomocą jednego wiersza kodu:
W twoim przypadku:
Aby to uprościć, możesz napisać taką funkcję:
Wyjaśnienie:
Konstruktor funkcji tworzy nowy obiekt Function. W JavaScript każda funkcja jest w rzeczywistości obiektem Function. Składnia do jawnego tworzenia funkcji za pomocą Konstruktora funkcji to:
gdzie
arguments(arg1 to argN)
musi być ciągiem, który odpowiada prawidłowemu identyfikatorowi javaScript ifunctionBody
jest ciągiem zawierającym instrukcje javaScript zawierające definicję funkcji.W naszym przypadku korzystamy z funkcji ciągu znaków, aby pobrać element obiektu z notacją kropkową.
Mam nadzieję, że to pomoże.
źródło
Odpowiedź GET / SET, która działa również w reakcji native (nie można
Object.prototype
obecnie przypisać ):Stosowanie:
źródło
Skopiowałem poniższe z odpowiedzi Ricardo Tomasi i zmodyfikowałem, aby również utworzyć podobiekty, które nie istnieją jeszcze w razie potrzeby. Jest to nieco mniej wydajne (więcej
if
si tworzenia pustych obiektów), ale powinno być całkiem dobre.Pozwoli nam to również robić
Object.prop(obj, 'a.b', false)
to, czego wcześniej nie mogliśmy. Niestety nadal nie pozwala nam to przypisaćundefined
... Jeszcze nie wiem, jak sobie z tym poradzić.źródło
Jeśli chcesz przekonwertować dowolny obiekt zawierający klucze notacji kropkowej na wersję tablicową tych kluczy, możesz tego użyć.
Spowoduje to konwersję czegoś takiego
do
źródło
Oto mój kod bez użycia
eval
. Łatwo to też zrozumieć.źródło
Tak, pytano 4 lata temu i tak, rozszerzenie prototypów bazowych zwykle nie jest dobrym pomysłem, ale jeśli wszystkie rozszerzenia są przechowywane w jednym miejscu, mogą być przydatne.
Oto mój sposób na zrobienie tego.
Teraz będziesz mógł uzyskać wszędzie zagnieżdżoną właściwość bez importowania modułu z funkcją lub funkcją kopiuj / wklej.
UPD. Przykład:
UPD 2. Kolejne rozszerzenie łamie mongoose w moim projekcie. Przeczytałem również, że może to przerwać jquery. Więc nigdy nie rób tego w następny sposób
źródło
Oto moja implementacja
Realizacja 1
Implementacja 2 (użycie redukcji tablicy zamiast plasterka)
Przykłady:
jak również może obsługiwać obiekty wewnątrz tablic
źródło
To jest moje rozszerzone rozwiązanie zaproponowane przez: ninjagecko
Dla mnie zwykły zapis ciągów nie był wystarczający, więc poniżej wersja obsługuje takie rzeczy jak:
indeks (obj, 'data.accounts [0] .address [0] .postcode');
źródło
Ryzykując pokonanie martwego konia ... Uważam, że jest to najbardziej przydatne w przemierzaniu zagnieżdżonych obiektów w celu odniesienia się tam, gdzie jesteś, w odniesieniu do obiektu podstawowego lub podobnego obiektu o tej samej strukturze. W tym celu jest to przydatne w przypadku funkcji przejścia przez obiekt zagnieżdżony. Zauważ, że użyłem tablicy do przechowywania ścieżki. Byłoby trywialne zmodyfikować to, aby użyć albo ścieżki ciągu, albo tablicy. Zauważ też, że możesz przypisać wartość „niezdefiniowana” do wartości, w przeciwieństwie do niektórych innych implementacji.
źródło
Użyłem tego kodu w moim projekcie
Stosowanie:
źródło
Kilka lat później odkryłem, że obsługuje zakres i tablicę. na przykład
a['b']["c"].d.etc
źródło
Nie jest jasne, jakie jest twoje pytanie. Biorąc pod uwagę twój przedmiot,
obj.a.b
dałbyś ci „2” tak jak jest. Jeśli chcesz manipulować ciągiem w celu użycia nawiasów, możesz to zrobić:źródło
a.b.c
i tak naprawdę nie osiąga tego, czego chcą. Chcą wartości, a nieeval
ścieżki.a.b.c
, ale masz rację, najwyraźniej chciał uzyskać / ustawić wartość właściwości naobj.a.b
. Pytanie było dla mnie mylące, ponieważ powiedział, że chce „przekonwertować ciąg”…oto moje 10 centów za przypadek, poniżej funkcja zostanie ustawiona / ustawiona na podstawie podanej ścieżki, .. na pewno możesz ją poprawić, usuń || i zamień na,
Object.hasOwnProperty
jeśli pomylnie troszczysz się o fałszywe wartości,przetestowałem go z
a.b.c
ab2.c{a:{b:[0,1,{c:7}]}}
i działa zarówno przy ustawianiu, jak i uzyskiwaniu :).Cheerz
źródło