JS 객체가 주어짐
var obj = { a: { b: '1', c: '2' } }
그리고 끈
"a.b"
문자열을 점 표기법으로 변환하여 어떻게 갈 수 있습니까?
var val = obj.a.b
문자열이 그대로 'a'라면 사용할 수 있습니다 obj[a]. 그러나 이것은 더 복잡합니다. 나는 간단한 방법이 있다고 생각하지만 현재 탈출하고 있습니다.
JS 객체가 주어짐
var obj = { a: { b: '1', c: '2' } }
그리고 끈
"a.b"
문자열을 점 표기법으로 변환하여 어떻게 갈 수 있습니까?
var val = obj.a.b
문자열이 그대로 'a'라면 사용할 수 있습니다 obj[a]. 그러나 이것은 더 복잡합니다. 나는 간단한 방법이 있다고 생각하지만 현재 탈출하고 있습니다.
답변:
최근 메모 : 이 답변이 많은 찬사를 받았다는 것에 대해 아첨하지만, 나는 약간 끔찍합니다. "xabc"와 같은 점 표기법 문자열을 참조로 변환해야하는 경우, 아마도 이상한 직렬화 해제를 수행하지 않는 한 매우 잘못된 일이 있다는 신호일 수 있습니다.
다시 말해,이 답변을 찾는 초보자는 "왜 내가 이것을 하는가?"라는 질문을 스스로에게해야합니다.
유스 케이스가 작고 성능 문제가 발생하지 않을 경우 일반적 으로이 작업을 수행하는 것이 좋습니다. 추상화를 통해 나중에 더 복잡하게 만들 필요가 없습니다. 실제로 이것이 코드 복잡성을 줄이고 일을 단순하게 유지한다면 OP가 요구하는 작업을 수행 해야 할 것입니다. 그러나 그렇지 않은 경우 다음 중 하나라도 해당되는지 고려하십시오.
사례 1 : 데이터 작업의 기본 방법 (예 : 객체를 전달하고 역 참조하는 앱의 기본 형식) "문자열에서 함수 또는 변수 이름을 어떻게 찾을 수 있습니까?"
- 이것은 나쁜 프로그래밍 관행입니다 (특히 불필요한 메타 프로그래밍이 필요하며 기능에 영향을 미치지 않는 코딩 스타일을 위반하며 성능에 영향을 미칩니다). 이 경우에 자신을 찾는 초보자는 대신 배열 표현 (예 : [ 'x', 'a', 'b', 'c']) 또는 가능한 경우보다 직접 / 간단 / 직선적 인 작업을 고려해야합니다. 처음부터 참조 자체 추적 (클라이언트 측 또는 서버 측 인 경우 가장 이상적임) 등 (기존의 고유 ID는 추가하기에 부적합하지만 사양에 다른 방식으로 요구되는 경우 사용할 수 있음) 존재하지 않습니다.)
사례 2 : 직렬화 된 데이터 작업 또는 사용자에게 표시 될 데이터 Date 개체 대신 날짜를 문자열 "1999-12-30"으로 사용하는 것과 같습니다 (주의하지 않으면 시간대 버그가 발생하거나 직렬화 복잡성이 추가 될 수 있음). 또는 당신은 무엇을하고 있는지 알고 있습니다.
- 아마도 괜찮을 것입니다. 점 문자열 "."이 없는지주의하십시오. 위생 처리 된 입력 조각에.
이 답변을 항상 사용하고 문자열과 배열간에 앞뒤로 변환하는 것을 발견하면 나쁜 경우 일 수 있으며 대안을 고려해야합니다.
다음은 다른 솔루션보다 10 배 더 짧은 우아한 하나의 라이너입니다.
function index(obj,i) {return obj[i]}
'a.b.etc'.split('.').reduce(index, obj)
[편집] 또는 ECMAScript 6에서 :
'a.b.etc'.split('.').reduce((o,i)=>o[i], obj)
(다른 사람들이 제안하는 것처럼 eval이 항상 나쁘다고 생각하지는 않지만 (보통은 아니지만)이 사람들은이 방법이 eval을 사용하지 않는다는 것을 기쁘게 생각합니다. 위와 obj.a.b.etc주어진 obj문자열을 찾을 수 "a.b.etc"있습니다.)
reduceECMA-262 표준 (5 판)에 있음에도 불구하고 여전히 사용을 두려워하는 사람들을 위해 다음과 같이 2 줄 재귀 구현이 있습니다.
function multiIndex(obj,is) { // obj,['1','2','3'] -> ((obj['1'])['2'])['3']
return is.length ? multiIndex(obj[is[0]],is.slice(1)) : obj
}
function pathIndex(obj,is) { // obj,'1.2.3' -> multiIndex(obj,['1','2','3'])
return multiIndex(obj,is.split('.'))
}
pathIndex('a.b.etc')
JS 컴파일러가 수행하는 최적화에 따라 중첩 된 함수가 일반적인 메소드 (클로저, 오브젝트 또는 글로벌 네임 스페이스에 배치)를 통해 모든 호출에서 재정의되지 않도록 할 수 있습니다.
편집 :
주석에서 흥미로운 질문에 대답하려면 :
이것을 어떻게 세터로 바꾸겠습니까? 경로로 값을 반환 할뿐만 아니라 새로운 값이 함수에 전송되면 설정합니까? – Swader 6 월 28 일 21:42
((!) 참고 : 그가 호출 규칙을 위반하는 것처럼 슬프게도, 세터와 객체를 반환 할 수없는, 주석 대신 같은 부작용이있는 일반 세터 스타일의 기능을 참조 할 것 index(obj,"a.b.etc", value)하고 obj.a.b.etc = value.)
reduce스타일은 정말 적합하지 않다, 그러나 우리는 재귀 구현을 수정할 수 있습니다 :
function index(obj,is, value) {
if (typeof is == 'string')
return index(obj,is.split('.'), value);
else if (is.length==1 && value!==undefined)
return obj[is[0]] = value;
else if (is.length==0)
return obj;
else
return index(obj[is[0]],is.slice(1), value);
}
데모:
> obj = {a:{b:{etc:5}}}
> index(obj,'a.b.etc')
5
> index(obj,['a','b','etc']) #works with both strings and lists
5
> index(obj,'a.b.etc', 123) #setter-mode - third argument (possibly poor form)
123
> index(obj,'a.b.etc')
123
... 개인적으로는 별도의 기능을 만드는 것이 좋습니다 setIndex(...). 나는 질문의 원래 포즈 .split가 문자열이 아닌 인덱스 배열 (에서 얻을 수 있음 ) 을 사용할 수 있어야한다는 쪽지를 끝내고 싶습니다 . 편의 기능에는 아무런 문제가 없습니다.
논평자가 물었다 :
배열은 어떻습니까? "ab [4] .cd [1] [2] [3]"와 같은 것? – 알렉스
자바 스크립트는 매우 이상한 언어입니다. 일반적으로 객체의 경우에만 그렇게 예를 들어, 자신의 재산의 키와 같은 문자열을 가질 수 x일반 객체 같았다 x={}다음 x[1]이 될 것입니다 x["1"]당신이 바로 ... 그래 읽기 ...
Javascript Arrays (자체 Object의 인스턴스)는와 같은 것을 할 수 있지만 특히 정수 키를 권장합니다 x=[]; x["puppy"]=5;.
그러나 일반적으로 (그리고 예외가 있습니다) x["somestring"]===x.somestring(허용되면; 할 수 없습니다 x.123).
(사용중인 JS 컴파일러가 사양을 위반하지 않는다는 것을 증명할 수있는 경우 더 세밀한 표현으로 컴파일 할 수 있음을 명심하십시오.)
따라서 귀하의 질문에 대한 답변은 해당 객체가 정수만을 허용한다고 가정하는지 (문제 도메인의 제한으로 인해) 여부에 달려 있습니다. 가정하지 말자. 이어서 올바른 표현은 기지국 식별자 더하기 일부를 연결 한 .identifierS 플러스 일부 ["stringindex"]들
a["b"][4]["c"]["d"][1][2][3]우리는 아마 지원해야하지만 이것 과 같습니다 a.b["c\"validjsstringliteral"][3]. 유효한 문자열 리터럴을 구문 분석하는 방법을 보려면 문자열 리터럴 에서 ecmascript 문법 섹션 을 확인해야합니다 . 기술적으로 당신 a은 유효한 자바 스크립트 식별자 인 ( 첫 번째 답변과 달리) 확인하고 싶습니다 .
귀하의 질문에 대한 대답은 간단하지만, 당신의 문자열 쉼표 나 괄호를 포함하지 않는 경우 , 그냥 세트의 문자 길이 1+ 시퀀스와 일치하는 것 ,또는 [나 ]:
> "abc[4].c.def[1][2][\"gh\"]".match(/[^\]\[.]+/g)
// ^^^ ^ ^ ^^^ ^ ^ ^^^^^
["abc", "4", "c", "def", "1", "2", ""gh""]
문자열에 이스케이프 문자 또는 "문자가 포함되어 있지 않고 IdentifierNames가 StringLiterals의 하위 언어이기 때문에 점을 []로 변환 할 수 있습니다.
> var R=[], demoString="abc[4].c.def[1][2][\"gh\"]";
> for(var match,matcher=/^([^\.\[]+)|\.([^\.\[]+)|\["([^"]+)"\]|\[(\d+)\]/g;
match=matcher.exec(demoString); ) {
R.push(Array.from(match).slice(1).filter(x=>x!==undefined)[0]);
// extremely bad code because js regexes are weird, don't use this
}
> R
["abc", "4", "c", "def", "1", "2", "gh"]
물론 항상 조심하고 데이터를 신뢰하지 마십시오. 일부 사용 사례에서 작동 할 수있는 몇 가지 나쁜 방법은 다음과 같습니다.
// hackish/wrongish; preprocess your string into "a.b.4.c.d.1.2.3", e.g.:
> yourstring.replace(/]/g,"").replace(/\[/g,".").split(".")
"a.b.4.c.d.1.2.3" //use code from before
스페셜 2018 편집 :
하자의 전체-원을 가서 우리가 가지고 올 수있는 가장 비효율적 인, 끔찍-overmetaprogrammed 솔루션 ... 구문의 이익을 할 순도 hamfistery을. ES6 Proxy 객체를 사용하여! ... 또한 잘 작성된 라이브러리를 손상시킬 수있는 일부 속성 (정상적이고 훌륭하지만)을 정의 해 봅시다. 성능, 온전함 (자신 또는 다른 사람), 직업 등에 관심이있는 경우이를 사용하는 것에주의해야합니다.
// [1,2,3][-1]==3 (or just use .slice(-1)[0])
if (![1][-1])
Object.defineProperty(Array.prototype, -1, {get() {return this[this.length-1]}}); //credit to caub
// WARNING: THIS XTREME™ RADICAL METHOD IS VERY INEFFICIENT,
// ESPECIALLY IF INDEXING INTO MULTIPLE OBJECTS,
// because you are constantly creating wrapper objects on-the-fly and,
// even worse, going through Proxy i.e. runtime ~reflection, which prevents
// compiler optimization
// Proxy handler to override obj[*]/obj.* and obj[*]=...
var hyperIndexProxyHandler = {
get: function(obj,key, proxy) {
return key.split('.').reduce((o,i)=>o[i], obj);
},
set: function(obj,key,value, proxy) {
var keys = key.split('.');
var beforeLast = keys.slice(0,-1).reduce((o,i)=>o[i], obj);
beforeLast[keys[-1]] = value;
},
has: function(obj,key) {
//etc
}
};
function hyperIndexOf(target) {
return new Proxy(target, hyperIndexProxyHandler);
}
데모:
var obj = {a:{b:{c:1, d:2}}};
console.log("obj is:", JSON.stringify(obj));
var objHyper = hyperIndexOf(obj);
console.log("(proxy override get) objHyper['a.b.c'] is:", objHyper['a.b.c']);
objHyper['a.b.c'] = 3;
console.log("(proxy override set) objHyper['a.b.c']=3, now obj is:", JSON.stringify(obj));
console.log("(behind the scenes) objHyper is:", objHyper);
if (!({}).H)
Object.defineProperties(Object.prototype, {
H: {
get: function() {
return hyperIndexOf(this); // TODO:cache as a non-enumerable property for efficiency?
}
}
});
console.log("(shortcut) obj.H['a.b.c']=4");
obj.H['a.b.c'] = 4;
console.log("(shortcut) obj.H['a.b.c'] is obj['a']['b']['c'] is", obj.H['a.b.c']);
산출:
obj는 { "a": { "b": { "c": 1, "d": 2}}}입니다.
(프록시 재정의 get) objHyper [ 'abc']는 다음과 같습니다. 1
(프록시 재정의 세트) objHyper [ 'abc'] = 3, 이제 obj는 { "a": { "b": { "c": 3, "d": 2}}}입니다.
(장면 뒤) objHyper는 다음과 같습니다. 프록시 {a : {…}}
(바로 가기) obj.H [ 'abc'] = 4
(바로 가기) obj.H [ 'abc']는 obj [ 'a'] [ 'b'] [ 'c']는 다음과 같습니다. 4
비효율적 인 아이디어 : 입력 인수를 기반으로 디스패치를 위의 내용을 수정할 수 있습니다. .match(/[^\]\[.]+/g)지원 하는 메소드를 사용 obj['keys'].like[3]['this']하거나 if instanceof Array인 경우 와 같이 Array를 입력으로 승인하십시오 keys = ['a','b','c']; obj.H[keys].
정의되지 않은 인덱스를 '더 부드러운'NaN 스타일 방식으로 처리하려는 경우 (예 : index({a:{b:{c:...}}}, 'a.x.c')잡히지 않은 TypeError가 아닌 undefined를 반환) ... :
1) 이는 1 차원 인덱스 상황에서 "오류를 던지기보다는 정의되지 않은 상태로 반환해야합니다"라는 관점에서 의미가 있습니다. { 'eg'] == undefined이므로 " N 차원 상황에서 오류입니다.
2) 이것은 우리가하고있는 관점에서 의미 가 없으므로x['a']['x']['c'] 위의 예제에서 TypeError로 실패합니다.
즉, 축소 기능을 다음 중 하나로 대체 하여이 작업을 수행 할 수 있습니다.
(o,i)=>o===undefined?undefined:o[i]또는
(o,i)=>(o||{})[i].
(다음에 색인 할 하위 결과가 정의되지 않을 때마다 for 루프를 사용하고 중단 / 복귀를 사용하거나 이러한 실패가 충분히 드문 것으로 예상되는 경우 try-catch를 사용하여이를보다 효율적으로 만들 수 있습니다.)
reduce현재 사용중인 모든 브라우저에서 지원되는 것은 아닙니다.
Array.reduceECMA-262 표준의 일부입니다. 오래된 브라우저를 실제로 지원하려면 Array.prototype.reduce어딘가에 주어진 샘플 구현을 정의 할 수 있습니다 (예 : developer.mozilla.org/en/JavaScript/Reference/Global_Objects/… ).
var setget = function( obj, path ){ function index( robj,i ) {return robj[i]}; return path.split('.').reduce( index, obj ); }
_.get(object, path)경로를 찾지 못하면 깨지지 않습니다. 'a.b.etc'.split('.').reduce((o,i)=>o[i], obj)그렇습니다. 모든 경우가 아니라 내 특정 사례에 대해 정확히 내가 필요한 것. 감사!
defaultValue있습니다. _.get()메소드가 리턴하는 경우 기본값 _.get()결의로는 undefined당신이 원하는 당신이 설정 한 값에 대한 시계 무엇에 그렇게 설정합니다.
_.set(object, path, value).
재귀와 관련된 좀 더 복잡한 예.
function recompose(obj,string){
var parts = string.split('.');
var newObj = obj[parts[0]];
if(parts[1]){
parts.splice(0,1);
var newString = parts.join('.');
return recompose(newObj,newString);
}
return newObj;
}
var obj = { a: { b: '1', c: '2', d:{a:{b:'blah'}}}};
alert(recompose(obj,'a.d.a.b')); //blah
lodash.get 을 사용할 수도 있습니다 .
이 패키지 (npm i --save lodash.get)를 설치 한 후 다음과 같이 사용하면됩니다.
const get = require('lodash.get');
const myObj = { user: { firstName: 'Stacky', lastName: 'Overflowy' }, id: 123 };
console.log(get(myObj, 'user.firstName')); // prints Stacky
console.log(get(myObj, 'id')); //prints 123
//You can also update values
get(myObj, 'user').firstName = John;
동일한 경로를 여러 번 역 참조 할 경우 각 점 표기법 경로에 대한 함수를 작성하면 실제로 최고의 성능을 발휘합니다 (위의 설명에서 James Wilkins와 연결된 성능 테스트를 확장).
var path = 'a.b.x';
var getter = new Function("obj", "return obj." + path + ";");
getter(obj);
Function 생성자를 사용하면 보안 및 최악의 성능 측면에서 eval ()과 동일한 단점이 있지만 IMO는 극도의 역동 성과 고성능의 조합이 필요한 경우에 잘 사용되지 않는 도구입니다. 이 방법을 사용하여 배열 필터 함수를 작성하고 AngularJS 다이제스트 루프 내에서 호출합니다. 내 프로파일은 3-4 레벨 깊이의 동적으로 정의 된 경로를 사용하여 약 2000 개의 복잡한 객체를 역 참조하고 필터링하는 데 1ms 미만의 array.filter () 단계를 일관되게 보여줍니다.
물론 setter 함수를 생성하기 위해 비슷한 방법론을 사용할 수 있습니다.
var setter = new Function("obj", "newval", "obj." + path + " = newval;");
setter(obj, "some new val");
원래 게시물 이후 몇 년. 이제 'object-path'라는 훌륭한 라이브러리가 있습니다. https://github.com/mariocasciaro/object-path
NPM 및 BOWER https://www.npmjs.com/package/object-path 에서 사용 가능
다음과 같이 쉽습니다.
objectPath.get(obj, "a.c.1"); //returns "f"
objectPath.set(obj, "a.j.0.f", "m");
깊이 중첩 된 속성 및 배열에 적용됩니다.
경로를 분할하고 반복하고 가지고있는 객체를 줄이는 것이 좋습니다. 이 제안은 누락 된 속성에 대한 기본값으로 작동 합니다.
const getValue = (object, keys) => keys.split('.').reduce((o, k) => (o || {})[k], object);
console.log(getValue({ a: { b: '1', c: '2' } }, 'a.b'));
console.log(getValue({ a: { b: '1', c: '2' } }, 'foo.bar.baz'));
다른 제안은 약간 비밀 스럽기 때문에 내가 기여할 것이라고 생각했습니다.
Object.prop = function(obj, prop, val){
var props = prop.split('.')
, final = props.pop(), p
while(p = props.shift()){
if (typeof obj[p] === 'undefined')
return undefined;
obj = obj[p]
}
return val ? (obj[final] = val) : obj[final]
}
var obj = { a: { b: '1', c: '2' } }
// get
console.log(Object.prop(obj, 'a.c')) // -> 2
// set
Object.prop(obj, 'a.c', function(){})
console.log(obj) // -> { a: { b: '1', c: [Function] } }
npm에 사용 가능한 라이브러리를 사용하면이 프로세스가 간단 해집니다. https://www.npmjs.com/package/dot-object
var dot = require('dot-object');
var obj = {
some: {
nested: {
value: 'Hi there!'
}
}
};
var val = dot.pick('some.nested.value', obj);
console.log(val);
// Result: Hi there!
var a = { b: { c: 9 } };
function value(layer, path, value) {
var i = 0,
path = path.split('.');
for (; i < path.length; i++)
if (value != null && i + 1 === path.length)
layer[path[i]] = value;
layer = layer[path[i]];
return layer;
};
value(a, 'b.c'); // 9
value(a, 'b.c', 4);
value(a, 'b.c'); // 4
이것은 훨씬 간단한 eval방법 과 비교할 때 많은 코드 이지만 Simon Willison이 말했듯이 eval을 사용해서는 안됩니다 .
또한 JSFiddle 입니다.
함수가 점 및 / 또는 배열 스타일 참조를 처리하고 빈 문자열로 인해 부모 객체가 반환되도록 ninjagecko의 우아한 답변을 확장했습니다.
여기 있습니다 :
string_to_ref = function (object, reference) {
function arr_deref(o, ref, i) { return !ref ? o : (o[ref.slice(0, i ? -1 : ref.length)]) }
function dot_deref(o, ref) { return ref.split('[').reduce(arr_deref, o); }
return !reference ? object : reference.split('.').reduce(dot_deref, object);
};
여기에서 작동하는 jsFiddle 예제를 참조하십시오 : http://jsfiddle.net/sc0ttyd/q7zyd/
[]표기법은 항상 배열에 대한 것으로 가정 합니다. 객체 키뿐만 아니라 예를가 그런 식으로 표현이있을 수 obj['some-problem/name'].list[1]이 나는 업데이트로했다 해결하기 위해 arr_deref이 같은 기능 javascript function arr_deref(o, ref, i) { return !ref ? o : (o[(ref.slice(0, i ? -1 : ref.length)).replace(/^['"]|['"]$/g, '')]); }
var find = function(root, path) {
var segments = path.split('.'),
cursor = root,
target;
for (var i = 0; i < segments.length; ++i) {
target = cursor[segments[i]];
if (typeof target == "undefined") return void 0;
cursor = target;
}
return cursor;
};
var obj = { a: { b: '1', c: '2' } }
find(obj, "a.b"); // 1
var set = function (root, path, value) {
var segments = path.split('.'),
cursor = root,
target;
for (var i = 0; i < segments.length - 1; ++i) {
cursor = cursor[segments[i]] || { };
}
cursor[segments[segments.length - 1]] = value;
};
set(obj, "a.k", function () { console.log("hello world"); });
find(obj, "a.k")(); // hello world
한 줄의 코드로 점 표기법으로 객체 멤버의 값을 얻을 수 있습니다.
new Function('_', 'return _.' + path)(obj);
당신의 경우 :
var obj = { a: { b: '1', c: '2' } }
var val = new Function('_', 'return _.a.b')(obj);
간단하게하기 위해 다음과 같은 함수를 작성할 수 있습니다.
function objGet(obj, path){
return new Function('_', 'return _.' + path)(obj);
}
설명:
Function 생성자는 새 Function 객체를 만듭니다. JavaScript에서 모든 함수는 실제로 함수 객체입니다. 함수 생성자를 사용하여 명시 적으로 함수를 작성하는 구문은 다음과 같습니다.
new Function ([arg1[, arg2[, ...argN]],] functionBody)
여기서 arguments(arg1 to argN)유효한 javaScript 식별자에 해당 functionBody하고 함수 정의를 구성하는 javaScript 문이 포함 된 문자열이어야합니다 .
이 경우 문자열 함수 본문을 활용하여 점 표기법으로 객체 멤버를 검색합니다.
도움이 되길 바랍니다.
반응 네이티브 에서도 작동하는 GET / SET 답변 ( Object.prototype현재 할당 할 수 없음 ) :
Object.defineProperty(Object.prototype, 'getNestedProp', {
value: function(desc) {
var obj = this;
var arr = desc.split(".");
while(arr.length && (obj = obj[arr.shift()]));
return obj;
},
enumerable: false
});
Object.defineProperty(Object.prototype, 'setNestedProp', {
value: function(desc, value) {
var obj = this;
var arr = desc.split(".");
var last = arr.pop();
while(arr.length && (obj = obj[arr.shift()]));
obj[last] = value;
},
enumerable: false
});
용법:
var a = { values: [{ value: null }] };
var b = { one: { two: 'foo' } };
a.setNestedProp('values.0.value', b.getNestedProp('one.two'));
console.log(a.values[0].value); // foo
Ricardo Tomasi의 답변 에서 다음을 복사하고 아직 존재하지 않는 하위 객체를 만들도록 수정했습니다. 약간 덜 효율적 if이지만 (더 많은 것들과 빈 객체 만들기) 꽤 좋을 것입니다.
또한 Object.prop(obj, 'a.b', false)이전에는 할 수 없었던 곳에서 할 수 있습니다. 불행히도 여전히 우리에게 할당 할 수는 없습니다 undefined... 아직 그 방법에 대해 잘 모르겠습니다.
/**
* Object.prop()
*
* Allows dot-notation access to object properties for both getting and setting.
*
* @param {Object} obj The object we're getting from or setting
* @param {string} prop The dot-notated string defining the property location
* @param {mixed} val For setting only; the value to set
*/
Object.prop = function(obj, prop, val){
var props = prop.split('.'),
final = props.pop(),
p;
for (var i = 0; i < props.length; i++) {
p = props[i];
if (typeof obj[p] === 'undefined') {
// If we're setting
if (typeof val !== 'undefined') {
// If we're not at the end of the props, keep adding new empty objects
if (i != props.length)
obj[p] = {};
}
else
return undefined;
}
obj = obj[p]
}
return typeof val !== "undefined" ? (obj[final] = val) : obj[final]
}
점 표기법 키가 포함 된 객체를 해당 키 의 배열 버전 으로 변환 하려면이 키를 사용할 수 있습니다.
이것은 다음과 같은 것을 변환합니다
{
name: 'Andy',
brothers.0: 'Bob'
brothers.1: 'Steve'
brothers.2: 'Jack'
sisters.0: 'Sally'
}
에
{
name: 'Andy',
brothers: ['Bob', 'Steve', 'Jack']
sisters: ['Sally']
}
convertDotNotationToArray(objectWithDotNotation) {
Object.entries(objectWithDotNotation).forEach(([key, val]) => {
// Is the key of dot notation
if (key.includes('.')) {
const [name, index] = key.split('.');
// If you have not created an array version, create one
if (!objectWithDotNotation[name]) {
objectWithDotNotation[name] = new Array();
}
// Save the value in the newly created array at the specific index
objectWithDotNotation[name][index] = val;
// Delete the current dot notation key val
delete objectWithDotNotation[key];
}
});
}
사용하지 않고 내 코드는 다음과 같습니다 eval. 이해하기도 쉽습니다.
function value(obj, props) {
if (!props) return obj;
var propsArr = props.split('.');
var prop = propsArr.splice(0, 1);
return value(obj[prop], propsArr.join('.'));
}
var obj = { a: { b: '1', c: '2', d:{a:{b:'blah'}}}};
console.log(value(obj, 'a.d.a.b')); //returns blah
예, 4 년 전에 요청을 받았으며 기본 프로토 타입을 확장하는 것은 일반적으로 좋은 생각은 아니지만 모든 확장을 한 곳에 보관하면 유용 할 수 있습니다.
자, 여기 내 길이 있습니다.
Object.defineProperty(Object.prototype, "getNestedProperty", {
value : function (propertyName) {
var result = this;
var arr = propertyName.split(".");
while (arr.length && result) {
result = result[arr.shift()];
}
return result;
},
enumerable: false
});
이제 함수 또는 복사 / 붙여 넣기 기능이있는 모듈을 가져 오지 않고도 어디서나 중첩 된 속성을 얻을 수 있습니다.
UPD. 예 :
{a:{b:11}}.getNestedProperty('a.b'); //returns 11
UPD 2. 다음 확장 프로그램이 내 프로젝트에서 몽구스를 깨뜨 렸습니다. 또한 jquery가 손상 될 수 있다는 것을 읽었습니다. 그러므로 절대로 다음 방법으로하지 마십시오
Object.prototype.getNestedProperty = function (propertyName) {
var result = this;
var arr = propertyName.split(".");
while (arr.length && result) {
result = result[arr.shift()];
}
return result;
};
여기 내 구현입니다
구현 1
Object.prototype.access = function() {
var ele = this[arguments[0]];
if(arguments.length === 1) return ele;
return ele.access.apply(ele, [].slice.call(arguments, 1));
}
구현 2 (슬라이스 대신 배열 축소 사용)
Object.prototype.access = function() {
var self = this;
return [].reduce.call(arguments,function(prev,cur) {
return prev[cur];
}, self);
}
예 :
var myobj = {'a':{'b':{'c':{'d':'abcd','e':[11,22,33]}}}};
myobj.access('a','b','c'); // returns: {'d':'abcd', e:[0,1,2,3]}
myobj.a.b.access('c','d'); // returns: 'abcd'
myobj.access('a','b','c','e',0); // returns: 11
또한 배열 내부의 객체를 처리 할 수 있습니다.
var myobj2 = {'a': {'b':[{'c':'ab0c'},{'d':'ab1d'}]}}
myobj2.access('a','b','1','d'); // returns: 'ab1d'
이것은 ninjagecko가 제안한 확장 솔루션입니다.
나를 위해 간단한 문자열 표기법으로는 충분하지 않으므로 아래 버전은 다음과 같은 것을 지원합니다.
index (obj, 'data.accounts [0] .address [0] .postcode');
/**
* Get object by index
* @supported
* - arrays supported
* - array indexes supported
* @not-supported
* - multiple arrays
* @issues:
* index(myAccount, 'accounts[0].address[0].id') - works fine
* index(myAccount, 'accounts[].address[0].id') - doesnt work
* @Example:
* index(obj, 'data.accounts[].id') => returns array of id's
* index(obj, 'data.accounts[0].id') => returns id of 0 element from array
* index(obj, 'data.accounts[0].addresses.list[0].id') => error
* @param obj
* @param path
* @returns {any}
*/
var index = function(obj, path, isArray?, arrIndex?){
// is an array
if(typeof isArray === 'undefined') isArray = false;
// array index,
// if null, will take all indexes
if(typeof arrIndex === 'undefined') arrIndex = null;
var _arrIndex = null;
var reduceArrayTag = function(i, subArrIndex){
return i.replace(/(\[)([\d]{0,})(\])/, (i) => {
var tmp = i.match(/(\[)([\d]{0,})(\])/);
isArray = true;
if(subArrIndex){
_arrIndex = (tmp[2] !== '') ? tmp[2] : null;
}else{
arrIndex = (tmp[2] !== '') ? tmp[2] : null;
}
return '';
});
}
function byIndex(obj, i) {
// if is an array
if(isArray){
isArray = false;
i = reduceArrayTag(i, true);
// if array index is null,
// return an array of with values from every index
if(!arrIndex){
var arrValues = [];
_.forEach(obj, (el) => {
arrValues.push(index(el, i, isArray, arrIndex));
})
return arrValues;
}
// if array index is specified
var value = obj[arrIndex][i];
if(isArray){
arrIndex = _arrIndex;
}else{
arrIndex = null;
}
return value;
}else{
// remove [] from notation,
// if [] has been removed, check the index of array
i = reduceArrayTag(i, false);
return obj[i]
}
}
// reduce with byIndex method
return path.split('.').reduce(byIndex, obj)
}
죽은 말을 때릴 위험이 있습니다 ... 이것은 중첩 된 객체를 횡단하여 기본 객체 또는 동일한 구조를 가진 유사한 객체와 관련된 위치를 참조하는 데 가장 유용합니다. 이를 위해 중첩 된 객체 순회 기능에 유용합니다. 경로를 유지하기 위해 배열을 사용했습니다. 문자열 경로 나 배열을 사용하도록 이것을 수정하는 것은 쉽지 않습니다. 또한 다른 구현과 달리 "정의되지 않은"값을 할당 할 수 있습니다.
/*
* Traverse each key in a nested object and call fn(curObject, key, value, baseObject, path)
* on each. The path is an array of the keys required to get to curObject from
* baseObject using objectPath(). If the call to fn() returns falsey, objects below
* curObject are not traversed. Should be called as objectTaverse(baseObject, fn).
* The third and fourth arguments are only used by recursion.
*/
function objectTraverse (o, fn, base, path) {
path = path || [];
base = base || o;
Object.keys(o).forEach(function (key) {
if (fn(o, key, o[key], base, path) && jQuery.isPlainObject(o[key])) {
path.push(key);
objectTraverse(o[key], fn, base, path);
path.pop();
}
});
}
/*
* Get/set a nested key in an object. Path is an array of the keys to reference each level
* of nesting. If value is provided, the nested key is set.
* The value of the nested key is returned.
*/
function objectPath (o, path, value) {
var last = path.pop();
while (path.length && o) {
o = o[path.shift()];
}
if (arguments.length < 3) {
return (o? o[last] : o);
}
return (o[last] = value);
}
내 프로젝트 에서이 코드를 사용했습니다.
const getValue = (obj, arrPath) => (
arrPath.reduce((x, y) => {
if (y in x) return x[y]
return {}
}, obj)
)
용법:
const obj = { id: { user: { local: 104 } } }
const path = [ 'id', 'user', 'local' ]
getValue(obj, path) // return 104
몇 년 후, 범위와 배열을 처리하는 것을 발견했습니다. 예 :a['b']["c"].d.etc
function getScopedObj(scope, str) {
let obj=scope, arr;
try {
arr = str.split(/[\[\]\.]/) // split by [,],.
.filter(el => el) // filter out empty one
.map(el => el.replace(/^['"]+|['"]+$/g, '')); // remove string quotation
arr.forEach(el => obj = obj[el])
} catch(e) {
obj = undefined;
}
return obj;
}
window.a = {b: {c: {d: {etc: 'success'}}}}
getScopedObj(window, `a.b.c.d.etc`) // success
getScopedObj(window, `a['b']["c"].d.etc`) // success
getScopedObj(window, `a['INVALID']["c"].d.etc`) // undefined
귀하의 질문이 무엇인지 명확하지 않습니다. 물체가 주어지면 obj.a.b그대로 "2"를 줄 것입니다. 대괄호를 사용하도록 문자열을 조작하려면 다음을 수행하십시오.
var s = 'a.b';
s = 'obj["' + s.replace(/\./g, '"]["') + '"]';
alert(s); // displays obj["a"]["b"]
a.b.c가 없으며 실제로 원하는 것을 달성하지 못합니다. 그들은 eval경로가 아닌 가치를 원합니다 .
a.b.c지만, 당신은 옳다. 그는에서 속성 값을 얻거나 설정하기를 원했다 obj.a.b. 그가 "줄을
여기에 내 10 센트가 있습니다. 벨로 프 기능은 제공된 경로를 기반으로 가져 오거나 설정됩니다. Object.hasOwnProperty실수로 잘못된 값을 신경 쓰면이를 대체하십시오 .
나는 그것을 a.b.cab2.c로 테스트하고 {a:{b:[0,1,{c:7}]}}설정과 가져 오기 모두에 대한 작품 :).
치어스
function helper(obj, path, setValue){
const l = String(path).split('.');
return l.reduce((o,i, idx)=>{
if( l.length-idx===1) { o[i] = setValue || o[i];return setValue ? obj : o[i];}
o[i] = o[i] || {};
return o[i];
}, x)
}
eval는 악하다. 사용하지 마십시오