문자열 경로로 중첩 된 JavaScript 객체 및 aray에 액세스


454

다음과 같은 데이터 구조가 있습니다.

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

그리고이 변수를 사용하여 데이터에 액세스하고 싶습니다 :

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

part1name someObject.part1.name은 'Part 1'인 값 으로 채워 져야합니다 . 60으로 채운 part2quantity와 같은 것.

순수한 자바 스크립트 또는 JQuery로 이것을 달성 할 수 있습니까?


여기서 무엇을 요구하는지 잘 모르십니까? part1.name을 조회하고 "part1.name"텍스트를 리턴 하시겠습니까? 아니면 part1.name에 저장된 값을 얻는 방법을 원하십니까?
— BonyT

당신은 같은 일을 시도했다 var part1name = someObject.part1name;`
— Rafay

1
@BonyT : someObject.part1.name을 쿼리하고 그 값 ( "Part 1")을 반환하려고합니다. 그러나 쿼리 ( "키"라고 함)를 변수 'part1name'에 저장하려고합니다. 답장을 보내 주셔서 감사합니다. @ 3nigma : 확실히 했어요. 그러나 그것은 나의 의도가 아니다. 답장을 보내 주셔서 감사합니다.
— Komaruloh 2016 년

1
중복 답변에서, 나는 fyr의 답변을 좋아합니다 stackoverflow.com/questions/8817394/…
— Steve Black

답변:


520

방금 이미 가지고있는 유사한 코드를 기반으로 이것을 만들었습니다. 작동하는 것처럼 보입니다.

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

용법::

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

http://jsfiddle.net/alnitak/hEsys/ 에서 실무 데모를보십시오.

EDIT 일부는 가장 왼쪽의 색인이 객체 내에 올바르게 중첩 된 항목에 해당하지 않는 문자열을 전달하면이 코드가 오류를 발생시키는 것으로 나타났습니다. 이것은 유효한 관심사이지만 try / catch,이 함수 undefined가 유효하지 않은 인덱스를 자동으로 리턴하는 것보다는 호출 할 때 IMHO가 블록 으로 처리하는 것이 가장 좋습니다 .


19
이것은 아름답게 작동합니다. 이것을 노드 패키지로 포장하여 인터넷에 제공하십시오.
— t3dodson

14
@ t3dodson 방금 수행 한 작업 : github.com/capaj/object-resolve-path 는 속성 이름에 '[]'가 포함되어 있으면이 기능이 제대로 작동하지 않습니다. 정규식은 그것을 '.'로 대체합니다. 예상대로 작동하지 않습니다
— Capaj

20
좋은 물건; lodash 라이브러리를 사용하여 다음을 수행 할 수도 있습니다._.get(object, nestedPropertyString);
— ian

17
이것은 주석의 바다에서 길을 잃을 수도 있지만 존재하지 않는 속성을 시도하고 해결하면 오류가 발생합니다. 그래서 'part3[0].name.iDontExist'. 확인에 o개체가 있는지 확인 if in하면 문제가 해결됩니다. (당신이 그것에 대해 어떻게 가는지는 당신에게 달려 있습니다). 업데이트 된 바이올린보기 : jsfiddle.net/hEsys/418
— ste2425

2
이것은 너무 금입니다. 우리는 구성 기반 응용 프로그램을 가지고 있으며 이것은 다소 도움이됩니다! 감사!
— Christian Esperar 2016 년

182

이것이 내가 사용하는 솔루션입니다.

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

사용법 예 :

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

한계 :

  • []구분 기호 토큰 (예 :) 사이에 배열 인덱스를 지정하면 .위와 같이 올바르게 작동 하지만 배열 인덱스에 대괄호 ( )를 사용할 수 없습니다 .

7
reduce를 사용하는 것은 훌륭한 솔루션입니다 ( _.reduce()밑줄 또는 lodash 라이브러리 에서도 사용 가능 )
— Alp

3
내 생각 엔 self아마 여기에 정의되어 있지 않습니다. 당신은 의미 this합니까?
— Platinum Azure

2
다음은 경로별로 값을 설정하는 보완 방법입니다. pastebin.com/jDp5sKT9
— mroach

1
누구나 이것을 TypeScript로 이식하는 방법을 알고 있습니까?
— Adam Plocher

1
@ SC1000 좋은 생각입니다. 이 답변은 대부분의 브라우저에서 기본 매개 변수를 사용할 수 있기 전에 작성되었습니다. 전역 객체를 기본값으로 참조하는 것은 의도적이므로 "function resolve (path, obj = self)"로 업데이트하겠습니다.
— speigg

179

이것은 이제 lodash를 사용하여 지원됩니다 _.get(obj, property). 참조 https://lodash.com/docs#get를

문서의 예 :

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
이것은 점과 괄호 구문 모두에서 작동하는 유일한 방법이므로 경로의 키 문자열에 '[]'가 있으면 실패하지 않습니다.
— Capaj

7
이. 또한, 그것은 지원합니다_.set(...)
— Josh C.

오브제를 찾지 못하면 어떻게 될까요?
— DDave

@DDave 객체로 전달 된 값이 정의되지 않았거나 객체가 아닌 경우 _.get제공된 객체에 키가 없을 때와 동일한 동작을 표시합니다. 예를 들어 _.get(null, "foo") -> undefined, _.get(null, "foo", "bar") -> "bar". 그러나이 동작은 문서에 정의되어 있지 않으므로 변경 될 수 있습니다.
— Ian Walker-Sperber

5
@Capaj you kiddin '? 누가 lodash를 원하지 않거나 사용할 수 없습니까?
— Andre Figueiredo

74

ES6 : Vanila JS에서 한 줄만 (오류 대신에 찾지 못하면 null을 반환합니다)

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

또는 예 :

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

false, 0 및 음수도 인식하고 기본값을 매개 변수로 사용하는 즉시 사용 가능한 기능

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

사용 예 :

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

보너스 :

경로 를 설정 하려면 (@ rob-gordon 요청) 다음을 사용할 수 있습니다.

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

예:

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

[]을 사용하여 배열에 액세스하십시오 .

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

예:

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

2
나는이 기술을 좋아한다. 이것은 정말 지저분하지만 할당 에이 기술을 사용하고 싶었습니다. 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
사용의 생각처럼 난 줄일 수 있지만 논리는 오프 것 0, undefined그리고 null값. 대신에를 {a:{b:{c:0}}}반환 null합니다 0. 아마도 null 또는 undefined를 명시 적으로 확인하면 이러한 문제가 해결 될 것입니다. (p,c)=>p === undefined || p === null ? undefined : p[c]
— SmujMaiku

안녕하세요 @SmujMaiku, "사용 준비"함수가 '0', 'undefined'및 'null'에 대해 올바르게 반환됩니다. 콘솔에서 방금 테스트했습니다. resolvePath ({a : {b : {c : 0}}}, ' abc ', null) => 0; 그것은 키 대신에 하나 이상의 검사를 피하기 값 자체의 존재 여부를 확인
— 아드리아누 Spadoni

여기 DEFAULTVALUE 작업, 사용하지 않았다 Reflect.has(o, k) ? ...(ES6 Reflect.has )하지만 일
— 앙드레 Figueiredo의

setPath마지막 선택기의 첫 항목에 값을 설정합니다. 예를 들어 let o = {}; setPath(o, 'a.a', 0), {a: 0}대신에 결과 가 {a: {a: 0}}됩니다. 해결책 은 이 게시물을 참조하십시오 .
— pkfm

62

문자열을 직접 파싱해야합니다.

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

또한 점 표기법으로 배열 인덱스를 정의해야합니다.

var part3name1 = "part3.0.name";

구문 분석이 쉬워집니다.

데모


@Felix Kling : 귀하의 솔루션이 필요한 것을 제공합니다. 그리고 정말 감사합니다. 그러나 Alnitak은 또한 다른 방법을 제공하며 작동하는 것처럼 보입니다. 답변은 하나만 선택할 수 있으므로 Alnitak 답변을 선택하겠습니다. 그의 해결책이 당신이나 그런 것보다 낫다는 것이 아닙니다. 어쨌든, 당신의 노력과 노력에 진심으로 감사드립니다.
— Komaruloh 2016 년

1
@ Komaruloh : 오, 나는 당신이 항상 당신 자신의 질문에 투표 답변을 올릴 수 있다고 생각했습니다 .... 어쨌든 나는 농담이었고, 더 많은 명성이 필요하지 않습니다.) 행복한 코딩!
— Felix Kling 2016 년

1
@Felix Kling : 투표하려면 최소 15 명 이상의 평판이 필요합니다. :) 나는 당신이 69k +로 더 많은 명성을 필요로하지 않는다고 생각합니다. 감사합니다
— Komaruloh

@Felix FWIW- []구문에서 속성 구문으로 변환하는 것은 쉽지 않습니다.
— Alnitak

4
while 루프를 while (l > 0 && (obj = obj[current]) && i < l)로 변경하면 이 코드는 점이없는 문자열에서도 작동합니다.
— Snea

39

객체 내부의 배열 / 배열에서도 작동합니다. 유효하지 않은 값에 방어 적입니다.

/**
 * 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
감사합니다. 최고의 답변입니다 -jsfiddle.net/Jw8XB/1
— Dominic

@Endless, 나는 항목을 점으로 구분 해야하는 경로를 강조하고 싶습니다. 괄호가 작동하지 않습니다. 즉, 배열의 첫 번째 항목에 액세스하려면 "0.sp ace"를 사용하십시오.
— TheZver

26

eval을 사용하여 :

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

랩은 에러시에 undefined를 돌려 준다

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

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

평가의 힘을 발휘할 때는 상식과주의를 기울이십시오. 가벼운 세이버와 비슷합니다. 전원을 켜면 사지가 끊어 질 확률이 90 %입니다. 모두를위한 것은 아닙니다.


7
eval이 좋은 아이디어인지 여부는 특성 문자열 데이터의 출처에 따라 다릅니다. 정적 "var p = 'abc'; eval (p);"를 통해 침입하는 해커에 대해 걱정할만한 이유가 있는지 의심합니다. 통화를 입력하십시오. 그것은 완벽하게 좋은 생각입니다.
— 제임스 윌킨스

17

다음과 같은 간단한 트릭으로 외부 JavaScript 라이브러리없이 도트 표기법을 사용하여 심층 객체 멤버의 가치를 얻을 수 있습니다.

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

귀하의 경우의 값을 구하는 part1.name에서 someObject바로 수행하십시오 :

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

간단한 바이올린 데모는 다음과 같습니다. https://jsfiddle.net/harishanchu/oq5esowf/


3
function deep_value (obj, path) {return new Function ( 'o', 'return o.'+ path) (obj); }
— ArcangelZith

14

이것은 아마도 하루의 빛을 보지 못할 것입니다 ... 그러나 여기는 어쨌든 있습니다.

  1. 교체 []브래킷 구문.
  2. .캐릭터 분할
  3. 빈 문자열 제거
  4. 경로를 찾으십시오 (그렇지 않으면 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

lodash가있는 하나의 라이너입니다.

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"

아니면 더 나은 ...

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

또는 ES6 버전 / 축소 ...

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

플 런커


7

나는 당신이 이것을 요구한다고 생각합니다 :

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

당신은 이것을 요청할 수 있습니다 :

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

둘 다 작동합니다


아니면 당신이 이것을 요구하고 있습니다

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

var part1name = someObject[partName][nameStr];

마지막으로 당신은 이것을 요구할 수 있습니다

var partName = "part1.name";

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

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

OP가 마지막 솔루션을 요구한다고 생각합니다. 그러나 문자열에는 Split메서드가 없지만 오히려 split있습니다.
— duri

사실 나는 마지막을 요구했다. partName 변수는 키 구조를 값으로 나타내는 문자열로 채워집니다. 귀하의 솔루션이 합리적입니다. 그러나 4-5 레벨 등과 같이 데이터의 깊이를 확장하기 위해 수정해야 할 수도 있습니다. 그리고 배열과 객체를 이것으로 균일하게 처리 할 수 ​​있는지 궁금합니다.
— Komaruloh 2016 년

7

여기에 여러 가지면에서 더 빠른 것처럼 보이는 더 많은 방법이 있습니다.

옵션 1 :에서 문자열 분할. 또는 [또는] 또는 '또는 ", 반대로 뒤집거나 빈 항목을 건너 뜁니다.

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

옵션 2 (를 제외하고 가장 빠름 eval) : 낮은 수준의 문자 스캔 (정규식 / 분할 / 등이없고 빠른 문자 스캔). 참고 :이 인덱스에는 따옴표를 지원하지 않습니다.

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)

옵션 3 : ( 신규 : 옵션 2가 따옴표를 지원하도록 확장 됨-조금 느리지 만 여전히 빠름)

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 (...)"은 여전히 ​​왕입니다 (성능은 현명합니다). 직접 제어 할 수있는 속성 경로가있는 경우 'eval'사용에 문제가 없어야합니다 (특히 속도가 필요한 경우). 속성 와이어를 "와이어 위로"( 줄에 !? lol : P) 당기면 안전합니다. 그것을 사용해야 할 충분한 이유 가 있기 때문에 바보 만이 "평가"를 전혀 사용하지 말라고 말할 것입니다. 또한 " Doug Crockford의 JSON 파서 에서 사용됩니다 ." 입력이 안전하다면 아무런 문제가 없습니다. 올바른 작업에 적합한 도구를 사용하십시오.


6

이런 경우에, 사람의 나중에 2017 년에이 질문을 방문하고 찾고 기억하기 쉬운 방법으로, 여기에 정교한 블로그 게시물의 액세스 중첩 된 자바 스크립트의 객체 에 의해 속여 않고

정의되지 않은 오류 의 'foo'속성을 읽을 수 없습니다

Array Reduce를 사용하여 중첩 된 객체에 액세스

이 예제 구조를 보자

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

중첩 배열에 액세스하려면 자신 만의 array reduce util을 작성할 수 있습니다.

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.

또한이 모든 것을 수행 하는 최소한의 라이브러리 typy 를 처리하는 훌륭한 유형 이 있습니다.

typy를 사용하면 코드가 다음과 같이 보입니다.

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

면책 조항 : 나는이 패키지의 저자입니다.


6

AngularJS

Speigg의 접근 방식은 매우 깔끔하고 깨끗하지만 문자열 경로로 AngularJS $ scope 속성에 액세스하는 솔루션을 검색하는 동안이 회신을 찾았으며 약간 수정하면 작업을 수행합니다.

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

이 함수를 루트 컨트롤러에 넣고 다음과 같은 하위 범위에서 사용하십시오.

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

AngularJS$scope.$eval 로 다른 방법으로 AngularJS를 사용 하는 방법을 참조하십시오 .
— georgeawg

3

문자열 경로로 모든 작업을 수행 할 수있는 패키지를 아직 찾지 못했기 때문에 insert (), get () (기본 반환), set () 및 remove ( ) 작업.

점 표기법, 대괄호, 숫자 인덱스, 문자열 번호 속성 및 단어가 아닌 문자가있는 키를 사용할 수 있습니다. 아래 간단한 사용법 :

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

와 일하다

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

문자열 또는 배열 경로를 허용하는 간단한 기능.

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

또는

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

답변으로 코드를 게시 하려는 경우 코드가 질문에 답변하는 이유 를 설명하십시오 .
— Tieson T.


2

감소는 좋지만 아무도 사용하지 않은 것에 놀랐습니다.

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

테스트


obj [key]가 실제로 존재하는지 확인조차하지 않습니다. 신뢰할 수 없습니다.
— Carles Alcolea

@CarlesAlcolea 기본적으로 js는 객체의 키가 있는지 확인 a.b.c하지 않습니다 b. 객체에 속성이 없으면 예외가 발생 합니다. 잘못된 키 경로를 자동으로 무시하는 것이 필요한 경우 (권장하지 않음) forEach를이 keys.forEach((key)=> obj = (obj||{})[key]);
— 키로

나는 그것을 통해 중괄호가 누락 된 개체를 실행 내 나쁜 :)
— Carles Alcolea

1

최근에 같은 질문이 있었고 객체 / 배열 도 중첩 된 https://npmjs.org/package/tea-properties 를 성공적으로 사용했습니다 set.

가져 오기:

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

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

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

세트:

var o = {};

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

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

"이 모듈은 단종되었습니다. chaijs / pathval을 사용하십시오."
— Patrick Fisher

1

@webjay의 답변에서 영감을 얻었습니다 : https://stackoverflow.com/a/46008856/4110122

이 기능을 사용 하여 객체의 값을 가져 오기 / 설정 / 설정 해제 할 수 있습니다

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

그것을 사용하려면 :

 // 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로 온라인 상점을 개발 중입니다. 제출시 원래 상태를 업데이트하기 위해 복사 된 상태 객체의 값을 변경하려고했습니다. 위의 예제는 대부분 복사 된 객체의 구조를 변경하기 때문에 효과가 없었습니다. I는 액세스 및 깊은 중첩 객체 속성의 값을 변경하기위한 함수의 일례를 작동 실측치 : https://lowrey.me/create-an-object-by-path-in-javascript-2/ 여기있다 :

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의 답변을 기반으로 합니다.

폴리 필을 검사에 싸서 기능을 단일 체인 축소로 줄였습니다.

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

코딩 타임에 알지 못하고 다른 중첩 키에 액세스 해야하는 경우 (키를 다루는 것이 쉽지 않습니다) 배열 표기법 접근자를 사용할 수 있습니다.

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

이들은 점 표기법 접근 자와 동일하며 런타임에 다음과 같이 달라질 수 있습니다.

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

var part1name = someObject[part][property];

에 해당

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

또는

var part1name = someObject.part1.name;

나는 이것이 당신의 질문을 해결하기를 바랍니다 ...

편집하다

객체 값에 액세스하기 위해 일종의 xpath 쿼리 를 유지 하기 위해 문자열을 사용하지 않습니다 . 쿼리를 구문 분석하고 값을 검색하는 함수를 호출해야하므로 다른 경로를 따르지 않습니다.

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

또는 apply 메소드 가 마음에 들지 않으면

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

함수는 더 짧고 명확하며 인터프리터는 구문 오류 등을 확인합니다.

그건 그렇고, 적절한시기에 간단한 과제가 충분하다고 생각합니다 ...


흥미 롭군 그러나 제 경우에는 part1name에 값을 할당 할 때 someObject가 아직 초기화되지 않았습니다. 나는 그 구조 만 알고 있습니다. 그렇기 때문에 문자열을 사용하여 구조를 설명합니다. 그리고 그것을 사용하여 someObject에서 내 데이터를 쿼리 할 수 ​​있기를 바랍니다. 의견을 보내 주셔서 감사합니다. :)
— 코마 룰로

@ Komaruloh : 변수를 만들 때 객체가 아직 초기화되지 않았다고 생각합니다. 내가 요점을 얻지 못하는 이유는 왜 적절한 시간에 과제를 수행 할 수 없습니까?
— 아이 네키

someObject가 아직 초기화되지 않았다고 언급하지 않아서 죄송합니다. 이유에 따라 someObject는 웹 서비스를 통해 가져옵니다. 그리고 part1name, part2qty 등으로 구성된 헤더 배열을 원합니다. 헤더 배열을 반복하고 part1name 값을 기준으로 'key'/ someObject의 경로로 원하는 값을 얻을 수 있습니다.
— Komaruloh 2016 년

0

여기의 솔루션은 깊이 중첩 된 키에 액세스하기위한 것입니다. 키에 액세스, 추가, 수정 및 삭제하기 위해 하나가 필요했습니다. 이것이 내가 생각해 낸 것입니다.

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: 배열의 경로. 로 교체 할 수 있습니다 string_path.split(".").
  • type_of_function: 액세스를 위해 0 (값을 전달하지 마십시오) value 않음), 추가 및 수정을 위해 0. 삭제하려면 1입니다.

0

Alnitak의 답변을 바탕으로 :

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

}

이를 통해 값을 설정할 수도 있습니다!

이것으로 npm 패키지 와 github 을 만들었습니다.


0

문자열 대신 배열을 중첩 된 객체와 배열에 사용할 수 있습니다. 예 : ["my_field", "another_field", 0, "last_field", 10]

다음은이 배열 표현을 기반으로 필드를 변경하는 예입니다. 중첩 된 구조의 상태를 변경하는 제어 된 입력 필드에 react.js와 같은 것을 사용하고 있습니다.

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

이전 답변을 바탕으로 대괄호를 처리 할 수있는 함수를 만들었습니다. 그러나 분할로 인해 내부에 점이 없습니다.

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

작업 Underscore의 property나 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>

행운을 빕니다...

당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.