Jak uzyskać dostęp i przetwarzać zagnieżdżone obiekty, tablice lub JSON?

874

Mam zagnieżdżoną strukturę danych zawierającą obiekty i tablice. Jak mogę wyodrębnić informacje, tj. Uzyskać dostęp do określonej lub wielu wartości (lub kluczy)?

Na przykład:

var data = {
    code: 42,
    items: [{
        id: 1,
        name: 'foo'
    }, {
        id: 2,
        name: 'bar'
    }]
};

Jak mogę uzyskać dostęp namedo drugiego elementu w items?

Felix Kling
źródło
22
@Marcel: Należy odczytać jako „Mam zagnieżdżoną strukturę danych lub JSON, jak mogę uzyskać dostęp do określonej wartości?”. I znać różnicę, ale wiele osób nie może być i nie szukając „JSON” zamiast „obiekt”. Wiele pytań ma formę „jak mogę uzyskać dostęp do X w tym JSON”. Jedyne miejsce, w którym wspominam o JSON w mojej odpowiedzi, to gdzie wyjaśniam, co to jest. Jeśli masz sugestię, jak lepiej to przekazać, jestem cały w uszach.
Felix Kling
możliwy duplikat JSON znaleziony w JavaScript
Travis J

Odpowiedzi:

1159

Czynności wstępne

JavaScript ma tylko jeden typ danych, który może zawierać wiele wartości: Object . Array jest specjalną formą obiektu.

(Zwykły) Obiekty mają formę

{key: value, key: value, ...}

Tablice mają formę

[value, value, ...]

Zarówno tablice, jak i obiekty ujawniają key -> valuestrukturę. Klucze w tablicy muszą być numeryczne, podczas gdy dowolny ciąg może być używany jako klucz w obiektach. Pary klucz-wartość są również nazywane „właściwościami” .

Dostęp do właściwości można uzyskać za pomocą notacji kropkowej

const value = obj.someProperty;

lub notacja w nawiasach , jeśli nazwa właściwości nie byłaby prawidłową nazwą identyfikatora JavaScript [spec] lub nazwa jest wartością zmiennej:

// the space is not a valid character in identifier names
const value = obj["some Property"];

// property name as variable
const name = "some Property";
const value = obj[name];

Z tego powodu dostęp do elementów tablicy można uzyskać tylko za pomocą notacji w nawiasach:

const value = arr[5]; // arr.5 would be a syntax error

// property name / index as variable
const x = 5;
const value = arr[x];

Poczekaj ... a co z JSON?

JSON to tekstowa reprezentacja danych, podobnie jak XML, YAML, CSV i inne. Aby pracować z takimi danymi, należy najpierw przekonwertować je na typy danych JavaScript, tj. Tablice i obiekty (a właśnie wyjaśniono, jak z nimi pracować). Jak parsować JSON wyjaśniono w pytaniu Parse JSON w JavaScript? .

Dalsze materiały do ​​czytania

Jak uzyskać dostęp do tablic i obiektów jest podstawową wiedzą o JavaScript, dlatego wskazane jest przeczytanie MDN JavaScript Guide , zwłaszcza sekcji



Dostęp do zagnieżdżonych struktur danych

Zagnieżdżona struktura danych to tablica lub obiekt, który odnosi się do innych tablic lub obiektów, tzn. Jego wartościami są tablice lub obiekty. Dostęp do takich struktur można uzyskać, stosując kolejno notację kropkową lub nawiasową.

Oto przykład:

const data = {
    code: 42,
    items: [{
        id: 1,
        name: 'foo'
    }, {
        id: 2,
        name: 'bar'
    }]
};

Załóżmy, że chcemy uzyskać dostęp namedo drugiego elementu.

Oto jak możemy to zrobić krok po kroku:

Jak widzimy, datajest obiektem, dlatego możemy uzyskać dostęp do jego właściwości za pomocą notacji kropkowej. itemsNieruchomość jest dostępna w następujący sposób:

data.items

Wartością jest tablica, aby uzyskać dostęp do jej drugiego elementu, musimy użyć notacji nawiasowej:

data.items[1]

Ta wartość jest obiektem i ponownie używamy notacji kropkowej, aby uzyskać dostęp do namewłaściwości. W końcu otrzymujemy:

const item_name = data.items[1].name;

Alternatywnie, moglibyśmy użyć notacji nawiasowej dla dowolnej właściwości, szczególnie jeśli nazwa zawierała znaki, które spowodowałyby, że byłaby niepoprawna dla użycia notacji kropkowej:

const item_name = data['items'][1]['name'];

Próbuję uzyskać dostęp do nieruchomości, ale dostaję tylko z undefinedpowrotem?

W większości przypadków undefinedobiekt / tablica po prostu nie ma właściwości o tej nazwie.

const foo = {bar: {baz: 42}};
console.log(foo.baz); // undefined

Użyj console.loglub console.diri sprawdź strukturę obiektu / tablicy. Właściwość, do której próbujesz uzyskać dostęp, może być faktycznie zdefiniowana w zagnieżdżonym obiekcie / tablicy.

console.log(foo.bar.baz); // 42

Co się stanie, jeśli nazwy właściwości są dynamiczne i nie znam ich wcześniej?

Jeśli nazwy właściwości są nieznane lub chcemy uzyskać dostęp do wszystkich właściwości obiektu / elementów tablicy, możemy użyć pętli for...in [MDN] dla obiektów i pętli for [MDN] dla tablic, aby iterować wszystkie właściwości / elementy.

Obiekty

Aby iterować po wszystkich właściwościach data, możemy iterować po obiekcie w następujący sposób:

for (const prop in data) {
    // `prop` contains the name of each property, i.e. `'code'` or `'items'`
    // consequently, `data[prop]` refers to the value of each property, i.e.
    // either `42` or the array
}

W zależności od tego, skąd pochodzi obiekt (i co chcesz zrobić), w każdej iteracji może być konieczne sprawdzenie, czy właściwość jest rzeczywiście własnością obiektu, czy jest dziedziczoną właściwością. Możesz to zrobić za pomocą Object#hasOwnProperty [MDN] .

Alternatywnie do for...inz hasOwnPropertymożna użyć Object.keys [MDN], aby uzyskać tablicę nazw właściwości :

Object.keys(data).forEach(function(prop) {
  // `prop` is the property name
  // `data[prop]` is the property value
});

Tablice

Aby wykonać iterację po wszystkich elementach data.items tablicy , używamy forpętli:

for(let i = 0, l = data.items.length; i < l; i++) {
    // `i` will take on the values `0`, `1`, `2`,..., i.e. in each iteration
    // we can access the next element in the array with `data.items[i]`, example:
    // 
    // var obj = data.items[i];
    // 
    // Since each element is an object (in our example),
    // we can now access the objects properties with `obj.id` and `obj.name`. 
    // We could also use `data.items[i].id`.
}

Można również użyć for...indo iteracji po tablicach, ale istnieją powody, dla których należy tego unikać: Dlaczego „for (var item in list)” z tablicami jest uważane za złą praktykę w JavaScript? .

Wraz z rosnącą obsługą przeglądarki ECMAScript 5 metoda tablicowa forEach [MDN] staje się również interesującą alternatywą:

data.items.forEach(function(value, index, array) {
    // The callback is executed for each element in the array.
    // `value` is the element itself (equivalent to `array[index]`)
    // `index` will be the index of the element in the array
    // `array` is a reference to the array itself (i.e. `data.items` in this case)
}); 

W środowiskach obsługujących ES2015 (ES6) można również użyć pętli [MDN] , która działa nie tylko dla tablic, ale dla każdej iterowalnej :for...of

for (const item of data.items) {
   // `item` is the array element, **not** the index
}

W każdej iteracji for...ofbezpośrednio daje nam kolejny element iteracji, nie ma „indeksu”, do którego można by uzyskać dostęp lub z którego można by korzystać.


Co jeśli „głębia” struktury danych jest mi nieznana?

Oprócz nieznanych kluczy „głębia” struktury danych (tj. Ile ma zagnieżdżonych obiektów), może być również nieznana. Sposób uzyskiwania dostępu do głęboko zagnieżdżonych właściwości zwykle zależy od dokładnej struktury danych.

Ale jeśli struktura danych zawiera powtarzające się wzorce, np. Reprezentację drzewa binarnego, rozwiązanie zazwyczaj obejmuje rekursywny dostęp [Wikipedia] do każdego poziomu struktury danych.

Oto przykład, aby uzyskać pierwszy węzeł liścia drzewa binarnego:

function getLeaf(node) {
    if (node.leftChild) {
        return getLeaf(node.leftChild); // <- recursive call
    }
    else if (node.rightChild) {
        return getLeaf(node.rightChild); // <- recursive call
    }
    else { // node must be a leaf node
        return node;
    }
}

const first_leaf = getLeaf(root);

Bardziej ogólnym sposobem uzyskania dostępu do zagnieżdżonej struktury danych z nieznanymi kluczami i głębokością jest przetestowanie typu wartości i odpowiednie działanie.

Oto przykład, który dodaje wszystkie pierwotne wartości w zagnieżdżonej strukturze danych do tablicy (zakładając, że nie zawiera żadnych funkcji). Jeśli napotkamy obiekt (lub tablicę), po prostu wywołujemy toArrayponownie tę wartość (wywołanie rekurencyjne).

function toArray(obj) {
    const result = [];
    for (const prop in obj) {
        const value = obj[prop];
        if (typeof value === 'object') {
            result.push(toArray(value)); // <- recursive call
        }
        else {
            result.push(value);
        }
    }
    return result;
}



Pomocnicy

Ponieważ struktura złożonego obiektu lub tablicy niekoniecznie jest oczywista, możemy sprawdzić wartość na każdym kroku, aby zdecydować, jak przejść dalej. console.log [MDN] i console.dir [MDN] pomagają nam to zrobić. Na przykład (dane wyjściowe konsoli Chrome):

> console.log(data.items)
 [ Object, Object ]

Widzimy tutaj, że data.itemsjest to tablica z dwoma elementami, które są obiektami. W konsoli Chrome obiekty mogą być nawet powiększane i sprawdzane natychmiast.

> console.log(data.items[1])
  Object
     id: 2
     name: "bar"
     __proto__: Object

To mówi nam, że data.items[1]jest to obiekt, a po rozwinięciu widzimy, że ma on trzy właściwości id, nameoraz __proto__. Ta ostatnia jest wewnętrzną właściwością stosowaną w łańcuchu prototypów obiektu. Jednak łańcuch prototypów i dziedziczenie nie mieszczą się w tej odpowiedzi.

Felix Kling
źródło
3
Niektóre z linków tutaj naprawdę pytają, jak to zrobić w jQuery, co, żeby być uczciwym, upraszcza tutaj 1 lub 2 rzeczy. Nie jestem pewien, czy uczynić to bardziej megapostem, czy odpowiedzieć na nie osobno - podstawowe informacje o tym, czym jest obiekt, czym jest tablica, są zwykle pytane ...
Chris Moschini,
1
@ felix-kling Jedna rzecz ... z zagnieżdżonymi obiektami, na przykład let object = {a: 1, b: 2, c: { a: 3, b: 4 }};, zwraca tablicę zawierającą tablicę dla każdego zagnieżdżonego obiektu, w tym przypadku [ 1, 2, [ 3, 4 ] ]Czy nie byłoby lepiej używać konkatii w wywołaniu rekurencyjnym zamiast wypychania? (wymagający zmiany wyniku)
ElFitz
3
Jest to najbardziej szczegółowa odpowiedź, jaką kiedykolwiek widziałem na stosie przepełnienia stosu - i odpowiedziała na moje pytanie! Dzięki!
William Jones
ta jedna strona pozwoliła mi poznać różnicę między ARRAY a OBJ
4ni5
76

Możesz uzyskać do niego dostęp w ten sposób

data.items[1].name

lub

data["items"][1]["name"]

Oba sposoby są równe.

vitmalina
źródło
Tak, ale nie możesz robić danych [„items”]. 1. name
neaumusic
5
Pierwsza jest o wiele bardziej intuicyjna, czytelna i krótsza;) Wolę używać składni właściwości nawiasów tylko wtedy, gdy nazwa właściwości jest zmienna.
DanteTheSmith
35

W przypadku, gdy próbujesz uzyskać dostęp itemdo przykładowej struktury za pomocą idlub name, nie znając jej pozycji w tablicy, najłatwiejszym sposobem jest skorzystanie z biblioteki underscore.js :

var data = {
    code: 42,
    items: [{
        id: 1,
        name: 'foo'
    }, {
        id: 2,
        name: 'bar'
    }]
};

_.find(data.items, function(item) {
  return item.id === 2;
});
// Object {id: 2, name: "bar"}

Z mojego doświadczenia wynika, że używanie funkcji wyższego rzędu zamiast forlub for..inpętli powoduje, że kod jest łatwiejszy do zrozumienia, a zatem łatwiejszy w utrzymaniu.

Tylko moje 2 centy.

zasada holograficzna
źródło
29

Obiekty i tablice mają wiele wbudowanych metod, które mogą pomóc w przetwarzaniu danych.

Uwaga: w wielu przykładach używam funkcji strzałek . Są podobne do wyrażeń funkcyjnych , ale wiążą thiswartość leksykalnie.

Object.keys(), Object.values()(ES 2017) i Object.entries()(ES 2017)

Object.keys()zwraca tablicę kluczy obiektu, Object.values()zwraca tablicę wartości obiektu i Object.entries()zwraca tablicę kluczy obiektu i odpowiadających im wartości w formacie [key, value].

const obj = {
  a: 1
 ,b: 2
 ,c: 3
}

console.log(Object.keys(obj)) // ['a', 'b', 'c']
console.log(Object.values(obj)) // [1, 2, 3]
console.log(Object.entries(obj)) // [['a', 1], ['b', 2], ['c', 3]]

Object.entries() z pętlą for-of i destrukcją

const obj = {
  a: 1
 ,b: 2
 ,c: 3
}

for (const [key, value] of Object.entries(obj)) {
  console.log(`key: ${key}, value: ${value}`)
}

Bardzo wygodnie jest iterować wynik Object.entries()za pomocą pętli for-of i destrukcji .

Pętla For-of pozwala na iterację elementów tablicy. Składnia jest następująca for (const element of array)(możemy zastąpić constz varalbo let, ale lepiej do użytku const, jeśli nie zamierzamy modyfikować element).

Przypisanie destrukcyjne pozwala wyodrębnić wartości z tablicy lub obiektu i przypisać je do zmiennych. W tym przypadku const [key, value]oznacza, że ​​zamiast przypisywać [key, value]tablicę do element, przypisujemy pierwszy element tej tablicy do, keya drugi element do value. Jest to równoważne z tym:

for (const element of Object.entries(obj)) {
  const key = element[0]
       ,value = element[1]
}

Jak widać, restrukturyzacja sprawia, że ​​jest to o wiele prostsze.

Array.prototype.every() i Array.prototype.some()

Te every()metody powraca truejeśli określony powraca funkcję wywołania zwrotnego truedla każdego elementu macierzy. Te some()metody powraca trueJeśli określona funkcja zwraca zwrotna truedo pewnego (co najmniej jeden) elementu.

const arr = [1, 2, 3]

// true, because every element is greater than 0
console.log(arr.every(x => x > 0))
// false, because 3^2 is greater than 5
console.log(arr.every(x => Math.pow(x, 2) < 5))
// true, because 2 is even (the remainder from dividing by 2 is 0)
console.log(arr.some(x => x % 2 === 0))
// false, because none of the elements is equal to 5
console.log(arr.some(x => x === 5))

Array.prototype.find() i Array.prototype.filter()

Te find()metody zwraca się pierwszy element, który spełnia podane funkcję wywołania zwrotnego. filter()Sposób powraca tablicy wszystkie elementy, które spełnia podaną funkcję wywołania zwrotnego.

const arr = [1, 2, 3]

// 2, because 2^2 !== 2
console.log(arr.find(x => x !== Math.pow(x, 2)))
// 1, because it's the first element
console.log(arr.find(x => true))
// undefined, because none of the elements equals 7
console.log(arr.find(x => x === 7))

// [2, 3], because these elements are greater than 1
console.log(arr.filter(x => x > 1))
// [1, 2, 3], because the function returns true for all elements
console.log(arr.filter(x => true))
// [], because none of the elements equals neither 6 nor 7
console.log(arr.filter(x => x === 6 || x === 7))

Array.prototype.map()

map()Zwraca tablicę z wynikami wywołanej podaną funkcję wywołania zwrotnego elementów macierzy.

const arr = [1, 2, 3]

console.log(arr.map(x => x + 1)) // [2, 3, 4]
console.log(arr.map(x => String.fromCharCode(96 + x))) // ['a', 'b', 'c']
console.log(arr.map(x => x)) // [1, 2, 3] (no-op)
console.log(arr.map(x => Math.pow(x, 2))) // [1, 4, 9]
console.log(arr.map(String)) // ['1', '2', '3']

Array.prototype.reduce()

reduce()Sposób zmniejsza tablicę do pojedynczej wartości wywołując podaną funkcję wywołania zwrotnego z dwóch elementów.

const arr = [1, 2, 3]

// Sum of array elements.
console.log(arr.reduce((a, b) => a + b)) // 6
// The largest number in the array.
console.log(arr.reduce((a, b) => a > b ? a : b)) // 3

reduce()Sposobie, opcjonalny drugi parametr, który jest wartość początkowa. Jest to przydatne, gdy tablica, do której wywołujesz, reduce()może zawierać zero lub jeden element. Na przykład, jeśli chcielibyśmy stworzyć funkcję, sum()która przyjmuje tablicę jako argument i zwraca sumę wszystkich elementów, moglibyśmy zapisać to w ten sposób:

const sum = arr => arr.reduce((a, b) => a + b, 0)

console.log(sum([]))     // 0
console.log(sum([4]))    // 4
console.log(sum([2, 5])) // 7

Michał Perłakowski
źródło
To moja ulubiona odpowiedź. Możesz także dodać przykład dla pętli tylko zagnieżdżone dane, takie jakObject.keys(data["items"]).forEach(function(key) { console.log(data["items"][key].id); console.log(data["items"][key].name); });
SilverSurfer
25

Czasami pożądany może być dostęp do zagnieżdżonego obiektu za pomocą łańcucha. Na przykład proste podejście to pierwszy poziom

var obj = { hello: "world" };
var key = "hello";
alert(obj[key]);//world

Ale często nie jest tak w przypadku złożonego jsona. W miarę jak Json staje się bardziej złożony, podejścia do znajdowania wartości wewnątrz JSON również stają się złożone. Najlepsze jest rekurencyjne podejście do nawigacji w Json, a sposób wykorzystania tej rekurencji będzie zależeć od typu poszukiwanych danych. Jeśli w grę wchodzą instrukcje warunkowe, dobrym narzędziem może być wyszukiwanie w json .

Jeśli dostęp do właściwości jest już znany, ale ścieżka jest złożona, na przykład w tym obiekcie

var obj = {
 arr: [
    { id: 1, name: "larry" },    
    { id: 2, name: "curly" },
    { id: 3, name: "moe" }
 ]
};

I wiesz, że chcesz uzyskać pierwszy wynik tablicy w obiekcie, być może chciałbyś użyć

var moe = obj["arr[0].name"];

Spowoduje to jednak wyjątek, ponieważ nie ma właściwości obiektu o tej nazwie. Rozwiązaniem, które mogłoby to wykorzystać, byłoby spłaszczenie drzewnego aspektu obiektu. Można to zrobić rekurencyjnie.

function flatten(obj){
 var root = {};
 (function tree(obj, index){
   var suffix = toString.call(obj) == "[object Array]" ? "]" : "";
   for(var key in obj){
    if(!obj.hasOwnProperty(key))continue;
    root[index+key+suffix] = obj[key];
    if( toString.call(obj[key]) == "[object Array]" )tree(obj[key],index+key+suffix+"[");
    if( toString.call(obj[key]) == "[object Object]" )tree(obj[key],index+key+suffix+".");   
   }
 })(obj,"");
 return root;
}

Teraz złożony obiekt można spłaszczyć

var obj = previous definition;
var flat = flatten(obj);
var moe = flat["arr[0].name"];//moe

Oto jedno jsFiddle Demoz takich podejść.

Travis J
źródło
WTH chciałbyś użyć obj["arr[0].name"]zamiast obj.arr[0].name? Prawie nie potrzebujesz / nie chcesz zajmować się spłaszczonymi obiektami, z wyjątkiem serializacji.
Bergi,
@Bergi - Często widzę to pytanie, a ponieważ jest ono używane kanonicznie, opublikowałem odpowiedź na tę wersję. Jeśli można tego uniknąć, znacznie szybciej jest użyć obj.arr [0] .name, ale czasami ludzie chcą przekazywać akcesoria do ciągów znaków i jest to przykład takiego działania.
Travis J,
Urgh. Mimo to nie ma powodu, aby spłaszczyć cały obiekt tylko po to, aby użyć pojedynczej ścieżki łańcuchowej, można go po prostu przeanalizować i przeprowadzić dynamiczne wyszukiwanie.
Bergi,
14

To pytanie jest dość stare, więc stanowi współczesną aktualizację. Wraz z nadejściem ES2015 istnieją alternatywy, aby uzyskać potrzebne dane. Dostępna jest teraz funkcja zwana destrukcją obiektów w celu uzyskania dostępu do zagnieżdżonych obiektów.

const data = {
  code: 42,
  items: [{
    id: 1,
    name: 'foo'
  }, {
    id: 2,
    name: 'bar'
  }]
};

const {
  items: [, {
    name: secondName
  }]
} = data;

console.log(secondName);

Powyższy przykład tworzy zmienną wywoływaną secondNamez nameklucza z tablicy o nazwie items, samotny ,mówi pominąć pierwszy obiekt w tablicy.

W tym przypadku jest to prawdopodobnie przesada, ponieważ prosty dostęp do tablicy jest łatwiejszy do odczytania, ale przydaje się przy dzieleniu obiektów w ogóle.

To jest bardzo krótkie wprowadzenie do konkretnego przypadku użycia, na początku destrukcja może być niezwykłą składnią. Polecam przeczytanie dokumentacji Mozilli dotyczącej Destrukturyzacji, aby dowiedzieć się więcej.

Alex KeySmith
źródło
13

Aby uzyskać dostęp do zagnieżdżonego atrybutu, musisz podać jego nazwę, a następnie przeszukać obiekt.

Jeśli znasz już dokładną ścieżkę, możesz ją na stałe zakodować w skrypcie:

data['items'][1]['name']

te również działają -

data.items[1].name
data['items'][1].name
data.items[1]['name']

Gdy nie znasz dokładnie dokładnej nazwy, lub użytkownik jest tym, który podaje nazwę dla Ciebie. Następnie wymagane jest dynamiczne przeszukiwanie struktury danych. Niektórzy sugerowali tutaj, że wyszukiwania można dokonać za pomocą forpętli, ale istnieje bardzo prosty sposób na przejście przez ścieżkę Array.reduce.

const data = { code: 42, items: [{ id: 1, name: 'foo' }, { id: 2, name: 'bar' }] }
const path = [ 'items', '1', 'name']
let result = path.reduce((a,v) => a[v], data)

Ścieżka jest sposobem na powiedzenie: Najpierw weź obiekt za pomocą klucza items, którym jest tablica. Następnie weź 1element -st (0 tablic indeksu). Na koniec weź obiekt z kluczem namew tym elemencie tablicy, którym jest ciąg znaków bar.

Jeśli masz bardzo długą ścieżkę, możesz nawet String.splitułatwić to wszystko -

'items.1.name'.split('.').reduce((a,v) => a[v], data)

To jest po prostu JavaScript, bez użycia bibliotek stron trzecich, takich jak jQuery lub lodash.

Evgeny
źródło
13
var ourStorage = {


"desk":    {
    "drawer": "stapler"
  },
"cabinet": {
    "top drawer": { 
      "folder1": "a file",
      "folder2": "secrets"
    },
    "bottom drawer": "soda"
  }
};
ourStorage.cabinet["top drawer"].folder2; // Outputs -> "secrets"

lub

//parent.subParent.subsubParent["almost there"]["final property"]

Zasadniczo użyj kropki między każdym potomkiem, który rozwija się pod nim, a gdy masz nazwy obiektów złożone z dwóch ciągów znaków, musisz użyć notacji [„obj name”]. W przeciwnym razie wystarczy kropka;

Źródło: https://learn.freecodecamp.org/javascript-algorithms-and-data-structures/basic-javascript/accessing-nested-objects

aby to dodać, dostęp do zagnieżdżonych tablic wyglądałby tak:

var ourPets = [
  {
    animalType: "cat",
    names: [
      "Meowzer",
      "Fluffy",
      "Kit-Cat"
    ]
  },
  {
    animalType: "dog",
    names: [
      "Spot",
      "Bowser",
      "Frankie"
    ]
  }
];
ourPets[0].names[1]; // Outputs "Fluffy"
ourPets[1].names[0]; // Outputs "Spot"

Źródło: https://learn.freecodecamp.org/javascript-algorithms-and-data-structures/basic-javascript/accessing-nested-arrays/

Kolejny bardziej przydatny dokument opisujący powyższą sytuację: https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Basics#Bracket_notation

Dostęp do nieruchomości za pośrednictwem dot walking: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_Accessors#Dot_notation

Jasio
źródło
Chociaż ten link może odpowiedzieć na pytanie, lepiej dołączyć tutaj istotne części odpowiedzi i podać link w celach informacyjnych. Odpowiedzi zawierające tylko łącze mogą stać się nieprawidłowe, jeśli połączona strona ulegnie zmianie. - Z recenzji
Robert,
1
Zredagowałem post. Mimo że ludzie szybko udzielali złej opinii. Następnym razem powstrzymam się od udzielenia odpowiedzi.
Johnny,
1
@Riddick nie powstrzymuj się, upewnij się, że nie publikujesz tylko linku
reggaeguitar
12

Możesz użyć lodash _getfunkcji:

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

_.get(object, 'a[0].b.c');
// => 3
Siergiej
źródło
9

Użycie JSONPath byłoby jednym z najbardziej elastycznych rozwiązań, jeśli chcesz dołączyć bibliotekę: https://github.com/s3u/JSONPath (węzeł i przeglądarka)

W twoim przypadku ścieżka json to:

$..items[1].name

więc:

var secondName = jsonPath.eval(data, "$..items[1].name");
Andrejs
źródło
1
Użycie eval () nie jest dobrym rozwiązaniem. Zamiast tego można użyć funkcji pierwszej klasy.
pradeep gowda
8

Na wszelki wypadek, gdy ktoś odwiedza to pytanie w 2017 roku lub później i szuka łatwego do zapamiętania sposobu, oto skomplikowany post na blogu na temat uzyskiwania dostępu do obiektów zagnieżdżonych w JavaScript bez przeszkadzania przez

Nie można odczytać właściwości „foo” niezdefiniowanego błędu

1. Wzorzec dostępu do zagnieżdżonych obiektów Olivera Steele

Najłatwiejszym i najczystszym sposobem jest użycie wzoru dostępu do zagnieżdżonych obiektów Olivera Steele

const name = ((user || {}).personalInfo || {}).name;

Dzięki tej notacji nigdy się nie spotkasz

Nie można odczytać właściwości „name” niezdefiniowanej .

Zasadniczo sprawdzasz, czy użytkownik istnieje, jeśli nie, tworzysz pusty obiekt w locie. W ten sposób dostęp do klucza następnego poziomu będzie zawsze możliwy z istniejącego obiektu lub pustego obiektu , ale nigdy z nieokreślonego.

2. Uzyskaj dostęp do zagnieżdżonych obiektów za pomocą funkcji Array Reduce

Aby mieć dostęp do zagnieżdżonych tablic, możesz napisać własną tablicę, zmniejsz wykorzystanie.

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', 'addresses', 0, 'city']);
// this will return the city from the first address item.

Istnieje również doskonały typ obsługujący minimalne typy bibliotek, który robi to wszystko za Ciebie.

Dinesh Pandiyan
źródło
3
To pytanie dotyczy przede wszystkim istniejących właściwości dostępu. Jest już pytanie o to, o czym mówisz (i już o większość swoich rozwiązań): Bezpieczny dostęp do zagnieżdżonych obiektów JavaScript lub Uzyskiwanie dostępu do zagnieżdżonych obiektów JavaScript za pomocą klucza łańcuchowego . Ale w każdym razie: „Niestety nie można uzyskać dostępu do tablic zagnieżdżonych za pomocą tej sztuczki”. Dlaczego nie? Tablice są obiektami, więc powinno działać równie dobrze. Czy możesz podać przykład, w którym tak nie jest?
Felix Kling
1
@FelixKling Kiedy próbujemy uzyskać dostęp do tablic ze wzorem Olivera Steele'a, nie będziemy w stanie stworzyć tablicy na „n” długości w locie i uzyskać dostępu do n-tego indeksu bez błędu „niezdefiniowany”. Dawny. ((user || {}).address || new Array(3))[1].name
Dinesh Pandiyan,
3
Nie konsekwentnie stosujesz swój wzór. Oczywiście ...[1].barspowodowałby błąd, gdyby element 1nie istniał. Ale tak jest również w przypadku, ....foo.bargdyby foonie istniało. Musisz również „strzec” dostępu 1, tak jak „strzeżesz” każdego innego dostępu do nieruchomości. Tablica jest tylko obiektem. „Element tablicy” to tylko własność. Byłoby to właściwie zastosowane (((user || {}).address || {})[1] || {}).name.
Felix Kling
1
To jest świetne. Nie uderzyło mnie to w ten sposób. Dzięki @FelixKling, zaktualizuję posty na blogu.
Dinesh Pandiyan,
2
@DineshPandiyan powinieneś ujawnić, że jesteś autorem typów, właśnie przyszedłem tutaj po przeczytaniu twojego wpisu na blogu
reggaeguitar
8

Wolę JQuery. Jest czystszy i łatwy do odczytania.

$.each($.parseJSON(data), function (key, value) {
  alert(value.<propertyname>);
});
Rudy Hinojosa
źródło
7

Dostęp do dynamicznie wielopoziomowego obiektu.

var obj = {
  name: "john doe",
  subobj: {
    subsubobj: {
      names: "I am sub sub obj"
    }
  }
};

var level = "subobj.subsubobj.names";
level = level.split(".");

var currentObjState = obj;

for (var i = 0; i < level.length; i++) {
  currentObjState = currentObjState[level[i]];
}

console.log(currentObjState);

Działające skrzypce: https://jsfiddle.net/andreitodorut/3mws3kjL/

Andrei Todorut
źródło
6

Jeśli szukasz jednego lub więcej obiektów spełniających określone kryteria, masz kilka opcji za pomocą zapytania-js

//will return all elements with an id larger than 1
data.items.where(function(e){return e.id > 1;});
//will return the first element with an id larger than 1
data.items.first(function(e){return e.id > 1;});
//will return the first element with an id larger than 1 
//or the second argument if non are found
data.items.first(function(e){return e.id > 1;},{id:-1,name:""});

Są też singlei a, singleOrDefaultktóre działają podobnie jak firsti firstOrDefaultodpowiednio. Jedyną różnicą jest to, że rzucą, jeśli znaleziono więcej niż jedno dopasowanie.

dla dalszego wyjaśnienia zapytania-js możesz zacząć od tego postu

Rune FS
źródło
Chciałbym wiedzieć, jak można to poprawić. Chcesz zostawić komentarz?
Rune FS,
6

The Underscore js Way

Która jest biblioteką JavaScript, która zapewnia cały bałagan przydatnych functional programmingpomocników bez rozszerzania jakichkolwiek wbudowanych obiektów.

Rozwiązanie:

var data = {
  code: 42,
  items: [{
    id: 1,
    name: 'foo'
  }, {
    id: 2,
    name: 'bar'
  }]
};

var item = _.findWhere(data.items, {
  id: 2
});
if (!_.isUndefined(item)) {
  console.log('NAME =>', item.name);
}

//using find - 

var item = _.find(data.items, function(item) {
  return item.id === 2;
});

if (!_.isUndefined(item)) {
  console.log('NAME =>', item.name);
}
Mohan Dere
źródło
6

Stare pytanie, ale jak nikt nie wspomniał o lodash (tylko podkreślenie).

Jeśli używasz już lodash w swoim projekcie, myślę, że jest to elegancki sposób w złożonym przykładzie:

Wybierz 1

_.get(response, ['output', 'fund', 'data', '0', 'children', '0', 'group', 'myValue'], '')

taki sam jak:

Wybierz 2

response.output.fund.data[0].children[0].group.myValue

Różnica między pierwszą a drugą opcją polega na tym, że w Opcji 1, jeśli brakuje jednej z właściwości (niezdefiniowanej) na ścieżce, nie pojawia się błąd, zwraca trzeci parametr.

W przypadku filtru tablicowego lodash ma, _.find()ale wolałbym używać zwykłego filter(). Ale nadal myślę o powyższej metodzie_.get() jest bardzo przydatna podczas pracy z naprawdę złożonymi danymi. W przeszłości miałem do czynienia z bardzo złożonymi interfejsami API i było to przydatne!

Mam nadzieję, że może być przydatny dla osób, które szukają opcji manipulowania naprawdę złożonymi danymi, które sugeruje tytuł.

Thiago C. S Ventura
źródło
5

Nie sądzę, by pytający dotyczył tylko jednego zagnieżdżonego poziomu, dlatego przedstawiam poniższe demo, aby pokazać, jak uzyskać dostęp do węzła głęboko zagnieżdżonego obiektu Json. W porządku, znajdźmy węzeł o identyfikatorze „5”.

var data = {
  code: 42,
  items: [{
    id: 1,
    name: 'aaa',
    items: [{
        id: 3,
        name: 'ccc'
      }, {
        id: 4,
        name: 'ddd'
      }]
    }, {
    id: 2,
    name: 'bbb',
    items: [{
        id: 5,
        name: 'eee'
      }, {
        id: 6,
        name: 'fff'
      }]
    }]
};

var jsonloop = new JSONLoop(data, 'id', 'items');

jsonloop.findNodeById(data, 5, function(err, node) {
  if (err) {
    document.write(err);
  } else {
    document.write(JSON.stringify(node, null, 2));
  }
});
<script src="https://rawgit.com/dabeng/JSON-Loop/master/JSONLoop.js"></script>

dabeng
źródło
Jak uzyskać dostęp do zagnieżdżonego obiektu Json za pomocą zmiennych. data = {a: {b: 'ss'}}; klucz var = dane ab [klucz] nie działa
Pasupathi Rajamanickam
3

Możesz użyć składni, jsonObject.keyaby uzyskać dostęp do wartości. A jeśli chcesz uzyskać dostęp do wartości z tablicy, możesz użyć składni jsonObjectArray[index].key.

Oto przykłady kodu, aby uzyskać dostęp do różnych wartości, aby dać ci pomysł.

        var data = {
            code: 42,
            items: [{
                id: 1,
                name: 'foo'
            }, {
                id: 2,
                name: 'bar'
            }]
        };

        // if you want 'bar'
        console.log(data.items[1].name);

        // if you want array of item names
        console.log(data.items.map(x => x.name));

        // get the id of the item where name = 'bar'
        console.log(data.items.filter(x => (x.name == "bar") ? x.id : null)[0].id);

Rahul Vala
źródło
3

Podejście dynamiczne

W poniższej deep(data,key)funkcji możesz użyć dowolnego keyciągu znaków - w twoim przypadku items[1].name(możesz użyć notacji tablicowej [i]na dowolnym poziomie) - jeśli klucz jest nieprawidłowy, wówczas niezdefiniowany jest return.

Kamil Kiełczewski
źródło
2

Pythoniczne, rekurencyjne i funkcjonalne podejście do rozwiązywania dowolnych drzew JSON:

handlers = {
    list:  iterate,
    dict:  delve,
    str:   emit_li,
    float: emit_li,
}

def emit_li(stuff, strong=False):
    emission = '<li><strong>%s</strong></li>' if strong else '<li>%s</li>'
    print(emission % stuff)

def iterate(a_list):
    print('<ul>')
    map(unravel, a_list)
    print('</ul>')

def delve(a_dict):
    print('<ul>')
    for key, value in a_dict.items():
        emit_li(key, strong=True)
        unravel(value)
    print('</ul>')

def unravel(structure):
    h = handlers[type(structure)]
    return h(structure)

unravel(data)

gdzie dane to lista python (parsowana z ciągu tekstowego JSON):

data = [
    {'data': {'customKey1': 'customValue1',
           'customKey2': {'customSubKey1': {'customSubSubKey1': 'keyvalue'}}},
  'geometry': {'location': {'lat': 37.3860517, 'lng': -122.0838511},
               'viewport': {'northeast': {'lat': 37.4508789,
                                          'lng': -122.0446721},
                            'southwest': {'lat': 37.3567599,
                                          'lng': -122.1178619}}},
  'name': 'Mountain View',
  'scope': 'GOOGLE',
  'types': ['locality', 'political']}
]
pX0r
źródło
6
To pytanie dotyczy JavaScript, a nie Pythona. Nie jestem pewien, czy istnieje równoważne pytanie dla Pythona.
Felix Kling
2

Funkcja grep jQuery pozwala filtrować przez tablicę:

var data = {
    code: 42,
    items: [{
        id: 1,
        name: 'foo'
    }, {
        id: 2,
        name: 'bar'
    }]
};

$.grep(data.items, function(item) {
    if (item.id === 2) {
        console.log(item.id); //console id of item
        console.log(item.name); //console name of item
        console.log(item); //console item object
        return item; //returns item object
    }

});
// Object {id: 2, name: "bar"}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>


źródło
2
// const path = 'info.value[0].item'
// const obj = { info: { value: [ { item: 'it works!' } ], randominfo: 3 }  }
// getValue(path, obj)

export const getValue = ( path , obj) => {
  const newPath = path.replace(/\]/g, "")
  const arrayPath = newPath.split(/[\[\.]+/) || newPath;

  const final = arrayPath.reduce( (obj, k) => obj ?  obj[k] : obj, obj)
  return final;
}
Michael Dimmitt
źródło
2

W 2020 roku możesz użyć @ babel / plugin-wniosek-opcjonalne-łączenie w łańcuchy, dostęp do zagnieżdżonych wartości w obiekcie jest bardzo łatwy.

 const obj = {
 foo: {
   bar: {
     baz: class {
   },
  },
 },
};

const baz = new obj?.foo?.bar?.baz(); // baz instance

const safe = new obj?.qux?.baz(); // undefined
const safe2 = new obj?.foo.bar.qux?.(); // undefined

https://babeljs.io/docs/en/babel-plugin-proposal-optional-chaining

https://github.com/tc39/proposal-optional-chaining

Rathore
źródło
-4

Mój stringdatapochodzi z pliku PHP, ale nadal wskazuję tutaj var. Kiedy bezpośrednio wezmę do objniego swojego jsona , nic nie pokaże, dlatego umieściłem mój plik json jako

var obj=JSON.parse(stringdata); więc po tym dostaję messageobj i wyświetlam w polu alertu, a następnie otrzymuję, dataktóra jest tablicą json i przechowuję w jednej zmiennej, ArrObja następnie czytam pierwszy obiekt tej tablicy o kluczowej wartości takiej jak taArrObj[0].id

     var stringdata={
        "success": true,
        "message": "working",
        "data": [{
                  "id": 1,
                  "name": "foo"
         }]
      };

                var obj=JSON.parse(stringdata);
                var key = "message";
                alert(obj[key]);
                var keyobj = "data";
                var ArrObj =obj[keyobj];

                alert(ArrObj[0].id);
Manthan Patel
źródło
2
Przykład jest mylący, ponieważ stringjsonnie jest łańcuchem.
Felix Kling