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

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

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로 이것을 달성 할 수 있습니까?



답변

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

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가 블록 으로 처리하는 것이 가장 좋습니다 .


답변

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

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

한계 :

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

답변

이것은 이제 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'


답변

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


답변

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

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

구문 분석이 쉬워집니다.

데모


답변

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

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


답변

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 %입니다. 모두를위한 것은 아닙니다.