[javascript] 중첩 된 객체, 배열 또는 JSON에 액세스하고 처리하려면 어떻게해야합니까?

객체와 배열을 포함하는 중첩 된 데이터 구조가 있습니다. 정보를 추출하는 방법, 즉 특정 또는 여러 값 (또는 키)에 액세스하는 방법은 무엇입니까?

예를 들면 다음과 같습니다.

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

name의 두 번째 항목에 items어떻게 액세스 할 수 있습니까?



답변

예비

JavaScript에는 여러 값을 포함 할 수있는 하나의 데이터 유형 ( Object) 만 있습니다. 배열 객체의 특별한 형태이다.

(일반) 물체의 형태는

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

배열의 형태는

[value, value, ...]

배열과 객체 모두 key -> value구조를 노출합니다 . 배열의 키는 숫자 여야하지만 모든 문자열은 객체의 키로 사용할 수 있습니다. 키-값 쌍은 “properties” 라고도합니다 .

점 표기법을 사용하여 속성에 액세스 할 수 있습니다

const value = obj.someProperty;

또는 괄호 표기 , 속성 이름이 유효한 JavaScript 않을 것인지 식별자 이름 [사양] 또는 이름 변수 값이다 :

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

이런 이유로 배열 요소는 대괄호 표기법을 통해서만 액세스 할 수 있습니다.

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

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

잠깐만 … JSON은 어떻습니까?

JSON은 XML, YAML, CSV 등과 같은 텍스트의 텍스트 표현입니다. 이러한 데이터로 작업하려면 먼저 배열 및 객체와 같은 JavaScript 데이터 유형으로 변환해야합니다 (그리고 이러한 작업을 수행하는 방법은 방금 설명했습니다). JSON을 구문 분석하는 방법 은 JavaScript에서 JSON 구문 분석 질문에 설명되어 있습니다 . .

추가 자료

배열과 객체에 액세스하는 방법은 기본적인 JavaScript 지식이므로 MDN JavaScript 안내서 , 특히 섹션 을 읽는 것이 좋습니다.



중첩 된 데이터 구조에 액세스

중첩 된 데이터 구조는 다른 배열 또는 객체를 참조하는 배열 또는 객체입니다. 즉, 값은 배열 또는 객체입니다. 도트 또는 브래킷 표기법을 연속적으로 적용하여 이러한 구조에 액세스 할 수 있습니다.

예를 들면 다음과 같습니다.

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

name두 번째 항목 에 액세스하려고한다고 가정 해 봅시다 .

다음은 단계별로 수행 할 수있는 방법입니다.

우리가 볼 수 있듯이 data객체는 점 표기법을 사용하여 속성에 액세스 할 수 있습니다. items속성은 다음과 같습니다 액세스 할 수 있습니다 :

data.items

값은 배열이며, 두 번째 요소에 액세스하려면 대괄호 표기법을 사용해야합니다.

data.items[1]

이 값은 객체이며 점 표기법을 다시 사용하여 name속성 에 액세스합니다 . 결국 우리는 다음을 얻습니다.

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

또는 이름에 점 표기법 사용에 유효하지 않은 문자가 포함 된 경우 속성에 대괄호 표기법을 사용할 수 있습니다.

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

속성에 액세스하려고하는데 undefined다시 돌아 오나요?

대부분의 경우 undefined객체 / 어레이에는 해당 이름의 속성이 없습니다.

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

객체 / 배열의 구조를 사용 console.log하거나 console.dir검사하십시오. 액세스하려는 속성이 실제로 중첩 된 객체 / 배열에 정의되어있을 수 있습니다.

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

속성 이름이 동적이고 미리 알 수 없으면 어떻게합니까?

속성 이름을 알 수 없거나 배열의 객체 / 요소에 대한 모든 속성에 액세스하려는 경우 객체에 for...in [MDN] 루프를 사용하고 배열에 for [MDN] 루프를 사용하여 모든 속성 / 요소를 반복 할 수 있습니다.

사물

의 모든 속성을 반복하기 위해 다음과 같이 객체를data 반복 할 수 있습니다 .

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
}

객체의 위치와 수행하려는 작업에 따라 속성이 실제로 객체의 속성인지 아니면 상속 된 속성인지 각 반복에서 테스트해야 할 수도 있습니다. Object#hasOwnProperty [MDN]으로 이를 수행 할 수 있습니다 .

for...inwith 대신에 [MDN]hasOwnProperty사용 하여 속성 이름 배열 을 얻을 수 있습니다 .Object.keys

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

배열

data.items 배열 의 모든 요소를 ​​반복 하기 위해 for루프를 사용합니다 .

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`.
}

for...in배열을 반복 하는 데 사용할 수도 있지만 이것을 피해야하는 이유가 있습니다. 배열에서 ‘for (var item in list)’가 JavaScript에서 나쁜 습관으로 간주되는 이유는 무엇입니까? .

ECMAScript 5의 브라우저 지원이 증가함에 따라 배열 방법 forEach [MDN]도 흥미로운 대안이되었습니다.

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

ES2015 (ES6)를 지원하는 환경에서는 [MDN] 루프 를 사용할 수도 있습니다. [MDN] 루프는 배열뿐만 아니라 반복 가능한 작업 에도 사용할 수 있습니다 .for...of

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

각 반복 for...of에서 iterable의 다음 요소를 직접 제공하며 액세스하거나 사용할 “인덱스”가 없습니다.


데이터 구조의 “깊이”를 모르면 어떻게해야합니까?

알 수없는 키 외에도 데이터 구조의 “깊이”(예 : 중첩 된 개체 수)도 알 수 없습니다. 깊이 중첩 된 속성에 액세스하는 방법은 일반적으로 정확한 데이터 구조에 따라 다릅니다.

데이터 구조는 예를 들면 이진 트리의 표현의 반복 패턴을 포함하지만, 일반적으로 용액에 포함 재귀 [위키] 상기 데이터 구조의 각 레벨 액세스.

다음은 이진 트리의 첫 번째 리프 노드를 가져 오는 예입니다.

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

알 수없는 키와 깊이로 중첩 된 데이터 구조에 액세스하는보다 일반적인 방법은 값의 유형을 테스트하고 그에 따라 조치하는 것입니다.

다음은 중첩 된 데이터 구조 내의 모든 기본 값을 배열에 추가하는 예입니다 (함수를 포함하지 않는다고 가정). 객체 (또는 배열)가 발생하면 toArray해당 값을 다시 호출 합니다 (재귀 호출).

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



헬퍼

복잡한 객체 또는 배열의 구조가 반드시 명확하지는 않기 때문에 각 단계의 값을 검사하여 더 나아가는 방법을 결정할 수 있습니다. console.log [MDN]console.dir [MDN] 이이 작업을 도와줍니다. 예를 들어 (Chrome 콘솔의 출력) :

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

여기서 우리 data.items는 이것이 두 객체 인 두 개의 요소를 가진 배열 임을 알 수 있습니다. Chrome 콘솔에서 개체를 즉시 확장하고 검사 할 수도 있습니다.

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

이는 우리에게 data.items[1]객체이며, 확장 후 우리가 세 가지 속성을 가지고 있음을 볼 수 id, name__proto__. 후자는 객체의 프로토 타입 체인에 사용되는 내부 속성입니다. 그러나 프로토 타입 체인과 상속은이 답변의 범위를 벗어납니다.


답변

이 방법으로 액세스 할 수 있습니다

data.items[1].name

또는

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

두 가지 방법이 동일합니다.


답변

배열의 위치를 ​​모르 거나 or 에 item의해 예제 구조에서 에 액세스하려는 경우 underscore.js 라이브러리 를 사용하는 것이 가장 쉬운 방법입니다 .idname

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

내 경험에 비추어 볼 때 for또는 for..in루프 대신 고차 함수를 사용 하면 추론하기 쉽고 코드를 유지 관리하기가 더 쉽습니다.

그냥 내 2 센트.


답변

객체와 배열에는 데이터 처리에 도움이되는 내장 메서드가 많이 있습니다.

참고 : 많은 예제에서 화살표 함수를 사용하고 있습니다 . 그것들은 함수 표현식 과 비슷 하지만 this값을 어휘 적으로 바인딩합니다 .

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

Object.keys()객체의 키 Object.values()배열, 객체의 값 Object.entries()배열, 객체의 키 배열 및 해당 값을 형식으로 반환합니다 [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() for-of 루프와 파괴 할당

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

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

결과를 반복하는 것이 매우 편리합니다. Object.entries() 과를 에 대한-의 루프destructuring 할당 .

For-of 루프를 사용하면 배열 요소를 반복 할 수 있습니다. 구문은 for (const element of array)(우리가 대체 할 수 const와 함께varlet,하지만 사용하는 것이 좋습니다 const우리가 수정하지 않으려는 경우element ).

할당 제거를 사용하면 배열 또는 객체에서 값을 추출하여 변수에 할당 할 수 있습니다. 이 경우 배열을 const [key, value]에 할당하는 대신 해당 [key, value]배열 element의 첫 번째 요소를key 번째 요소를 두 번째 요소를에value . 다음과 같습니다 :

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

보시다시피, 파괴는 이것을 훨씬 간단하게 만듭니다.

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

every()방법 리턴 true지정된 콜백 함수가 리턴하는 경우 true에 대한 모든 어레이 소자. some()메소드가 복귀 true하는 경우 지정된 콜백 함수의 복귀 true에 대한 일부 (적어도 하나)의 요소.

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()Array.prototype.filter()

find()방법 리턴 만족시키는 제공된 콜백 함수 소자. 이 filter()메소드는 제공된 콜백 함수를 만족시키는 모든 요소 의 배열을 리턴합니다 .

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()메소드는 배열 요소에서 제공된 콜백 함수를 호출 한 결과와 함께 배열을 리턴합니다.

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()메소드는 두 개의 요소로 제공된 콜백 함수를 호출하여 배열을 단일 값으로 줄입니다.

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()방법은 선택적인 두 번째 매개 변수 (초기 값)를 사용합니다. 이것은 호출하는 배열에 reduce()0 개 또는 1 개의 요소가 있을 수있는 경우에 유용 합니다. 예를 들어, sum()배열을 인수로 사용하고 모든 요소의 합을 반환 하는 함수를 만들려면 다음과 같이 작성할 수 있습니다.

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


답변

때로는 문자열을 사용하여 중첩 객체에 액세스하는 것이 바람직 할 수 있습니다. 간단한 접근 방식이 첫 번째 수준입니다 (예 :

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

그러나 복잡한 json의 경우에는 종종 그렇지 않습니다. json이 더 복잡 해짐에 따라 json 내부의 값을 찾는 방법도 복잡해집니다. json을 탐색하는 재귀 적 접근 방식이 가장 좋으며 재귀를 활용하는 방법은 검색되는 데이터 유형에 따라 다릅니다. 조건문이 관련된 경우 json 검색 을 사용하는 것이 좋습니다.

액세스중인 속성이 이미 알려져 있지만이 개체와 같이 경로가 복잡한 경우

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

그리고 객체에서 배열의 첫 번째 결과를 얻고 싶다는 것을 알고 있습니다.

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

그러나 해당 이름을 가진 객체의 속성이 없으므로 예외가 발생합니다. 이것을 사용할 수있는 해결책은 객체의 트리 측면을 평평하게하는 것입니다. 이것은 재귀 적으로 수행 될 수 있습니다.

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

이제 복잡한 물체를 평평하게 할 수 있습니다

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

다음은 jsFiddle Demo이 접근법 중 하나 입니다.


답변

이 질문은 현대의 업데이트로 꽤 오래되었습니다. ES2015가 시작되면서 필요한 데이터를 확보 할 수있는 대안이 있습니다. 이제 중첩 객체에 액세스하기위한 객체 구조 해제라는 기능이 있습니다.

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

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

console.log(secondName);

위의 예제 는 외로운 이라는 배열 secondNamename키에서 호출 된 변수를 만듭니다.items, 의 첫 번째 개체는 건너 뜁니다.

특히 간단한 배열 접근이 읽기 쉬우므로이 예제에서는 너무 과잉 일 수 있지만 일반적으로 객체를 분리 할 때 유용합니다.

이것은 특정 유스 케이스에 대한 간략한 소개이며, 처음에는 구조를 파괴하는 것이 특이한 구문 일 수 있습니다. 자세한 내용은 Mozilla의 Destructuring Assignment 문서 를 읽는 것이 좋습니다 .


답변

중첩 된 속성에 액세스하려면 이름을 지정한 다음 객체를 검색해야합니다.

정확한 경로를 이미 알고 있다면 다음과 같이 스크립트로 하드 코딩 할 수 있습니다.

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

이것들도 작동합니다-

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

사전에 정확한 이름을 모르거나 사용자가 이름을 제공 한 사람인 경우. 그런 다음 데이터 구조를 통한 동적 검색이 필요합니다. 일부는 for루프를 사용하여 검색을 수행 할 수 있다고 제안 했지만을 사용하여 경로를 탐색하는 매우 간단한 방법이 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)

경로는 말할 수있는 방법입니다. 먼저 key로 객체를 가져 items오십시오. 그런 다음 1-st 요소 (0 개의 인덱스 배열)를 사용하십시오. 마지막 name으로 해당 배열 요소에 키가있는 객체를 가져옵니다 .이 문자열은 문자열입니다.bar . 입니다.

경로가 너무 길면 String.split이 모든 작업을보다 쉽게 ​​수행 할 수 있습니다.

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

이것은 jQuery 또는 lodash와 같은 타사 라이브러리를 사용하지 않고 일반 JavaScript입니다.