문자열 경로로 중첩 된 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

작업 UnderscorepropertypropertyOf:

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.