Truy cập các đối tượng JavaScript lồng nhau và các chuỗi bằng đường dẫn chuỗi


454

Tôi có một cấu trúc dữ liệu như thế này:

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

Và tôi muốn truy cập dữ liệu bằng các biến này:

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

part1name phải được điền với someObject.part1.namegiá trị của, đó là "Phần 1". Điều tương tự với part2quantity chứa 60.

Có cách nào để đạt được điều này với javascript hoặc JQuery thuần túy không?


Không chắc chắn những gì bạn đang hỏi ở đây? Bạn muốn có thể truy vấn part1.name và trả lại văn bản "part1.name"? Hoặc bạn muốn một phương tiện để có được giá trị được lưu trữ trong part1.name?
BonyT

bạn đã thử làm như var part1name = someObject.part1name;`
Rafay

1
@BonyT: Tôi muốn truy vấn someObject.part1.name và trả về giá trị của nó ("Phần 1"). Tuy nhiên, tôi muốn truy vấn (tôi gọi nó là "khóa") được lưu trữ trong một biến 'part1name'. Cảm ơn vì đã trả lời. @ 3nigma: Tôi chắc chắn đã làm. Nhưng đó không phải là ý định của tôi. Cảm ơn vi đa trả lơi.
Komaruloh

1
trong câu trả lời trùng lặp, tôi thích câu trả lời của fyr stackoverflow.com/questions/8817394/ trên
Steve Black

Câu trả lời:


520

Tôi chỉ thực hiện điều này dựa trên một số mã tương tự tôi đã có, nó dường như hoạt động:

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

Sử dụng::

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

Xem bản demo hoạt động tại http://jsfiddle.net/alnitak/hEsys/

EDIT một số người đã nhận thấy rằng mã này sẽ gây ra lỗi nếu thông qua một chuỗi trong đó hầu hết các chỉ mục bên trái không tương ứng với một mục được lồng chính xác trong đối tượng. Đây là một mối quan tâm hợp lệ, nhưng IMHO giải quyết tốt nhất với một try / catchkhối khi gọi, thay vì có chức năng này âm thầm trả về undefinedmột chỉ mục không hợp lệ.


19
Điều này làm việc rất đẹp. Hãy đóng góp điều này cho internet bằng cách gói nó như một gói nút.
t3dodson

14
@ t3dodson Tôi vừa mới làm: github.com/capaj/object-resolve-path chỉ cần lưu ý rằng điều này không chơi tốt khi tên tài sản của bạn chứa '[]'. Regex sẽ thay thế nó bằng '.' và nó không hoạt động như mong đợi
Capaj

20
công cụ tuyệt vời; sử dụng thư viện lodash, người ta cũng có thể làm:_.get(object, nestedPropertyString);
ian

17
Điều này có thể sẽ bị mất trong biển ý kiến, tuy nhiên nó có lỗi nếu bạn thử và xử lý một tài sản không tồn tại. Vì vậy 'part3[0].name.iDontExist'. Thêm một kiểm tra để xem nếu omột đối tượng trong if insửa chữa vấn đề. (Làm thế nào bạn đi về đó là tùy thuộc vào bạn). Xem fiddle cập nhật: jsfiddle.net/hEsys/418
ste2425

2
Đây là vàng quá. Chúng tôi có một ứng dụng dựa trên cấu hình và điều này khá hữu ích! Cảm ơn!
Christian Esperar

182

Đây là giải pháp tôi sử dụng:

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

Ví dụ sử dụng:

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

Hạn chế:

  • Không thể sử dụng dấu ngoặc ( []) cho các chỉ mục mảng, mặc dù chỉ định các chỉ mục mảng giữa mã thông báo phân tách (ví dụ .:) hoạt động tốt như được hiển thị ở trên.

7
sử dụng giảm là một giải pháp tuyệt vời (người ta cũng có thể sử dụng _.reduce()từ thư viện gạch dưới hoặc thư viện)
Alp

3
Tôi nghĩ selfcó lẽ không xác định ở đây. Ý bạn là thissao
Bạch kim Azure

2
Đây là phần bổ sung của tôi để đặt giá trị theo đường dẫn: pastebin.com/jDp5sKT9
mroach

1
Bất cứ ai cũng biết làm thế nào để chuyển cái này sang TypeScript?
Adam Plocher

1
@ SC1000 ý kiến ​​hay. Câu trả lời này được viết trước khi các tham số mặc định có sẵn trong hầu hết các trình duyệt. Tôi sẽ cập nhật nó thành "hàm giải quyết (đường dẫn, obj = self)", vì tham chiếu đối tượng toàn cầu như một mặc định là có chủ ý.
speigg

179

Điều này hiện được hỗ trợ bởi lodash bằng cách sử dụng _.get(obj, property). Xem https://lodash.com/docs#get

Ví dụ từ các tài liệu:

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
Đây phải là câu trả lời duy nhất được chấp nhận, bởi vì đây là câu trả lời duy nhất hoạt động cho cả cú pháp dấu chấm và dấu ngoặc và Nó không thất bại, khi chúng ta có '[]' trong chuỗi khóa trong đường dẫn.
Capaj

7
Điều này. Thêm vào đó, nó hỗ trợ_.set(...)
Josh C.

Điều gì hạnh phúc nếu không tìm thấy objet?
DDave

@DDave nếu giá trị được truyền dưới dạng đối tượng không được xác định hoặc không phải là một đối tượng, _.getsẽ hiển thị hành vi tương tự như khi không tìm thấy khóa nào trong đối tượng được cung cấp. ví dụ như _.get(null, "foo") -> undefined, _.get(null, "foo", "bar") -> "bar". Tuy nhiên, hành vi này không được xác định trong các tài liệu để có thể thay đổi.
Ian Walker-Sperber

5
@Capaj bạn kiddin '? Và ai không muốn / không thể sử dụng lodash?
Andre Figueiredo

74

ES6 : Chỉ có một dòng trong Vanila JS (nó trả về null nếu không tìm thấy thay vì đưa ra lỗi):

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

Hoặc ví dụ:

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

Với toán tử xích tùy chọn :

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

Để sẵn sàng sử dụng hàm cũng nhận ra số sai, 0 và số âm và chấp nhận các giá trị mặc định làm tham số:

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

Ví dụ để sử dụng:

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

Tiền thưởng :

Để đặt đường dẫn (Được yêu cầu bởi @ rob-gordon), bạn có thể sử dụng:

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

Thí dụ:

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

Mảng truy cập với [] :

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

Thí dụ:

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

2
Tôi yêu kỹ thuật này. Điều này thực sự lộn xộn nhưng tôi muốn sử dụng kỹ thuật này để phân công. 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
Tôi thích ý tưởng của việc sử dụng giảm nhưng logic của bạn dường như tắt cho 0, undefinednullgiá trị. {a:{b:{c:0}}}trả lại nullthay vì 0. Có lẽ rõ ràng kiểm tra null hoặc không xác định sẽ làm rõ những vấn đề này. (p,c)=>p === undefined || p === null ? undefined : p[c]
SmujMaiku

Xin chào abc ', null) => 0; Nó kiểm tra xem khóa có tồn tại thay vì chính giá trị đó tránh được nhiều hơn một lần kiểm tra hay không
Adriano Spadoni

ở đây defaultValue không hoạt động, sử dụng Reflect.has(o, k) ? ...(ES6 Reflect.has ) mặc dù đã hoạt động
Andre Figueiredo

setPathsẽ đặt giá trị ở lần xuất hiện đầu tiên của bộ chọn cuối cùng. Ví dụ let o = {}; setPath(o, 'a.a', 0)kết quả trong {a: 0}chứ không phải {a: {a: 0}}. xem bài này để biết giải pháp
pkfm

62

Bạn phải tự phân tích chuỗi:

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

Điều này yêu cầu bạn cũng xác định các chỉ mục mảng với ký hiệu dấu chấm:

var part3name1 = "part3.0.name";

Nó làm cho việc phân tích cú pháp dễ dàng hơn.

BẢN GIỚI THIỆU


@Felix Kling: Giải pháp của bạn cung cấp cho tôi những gì tôi cần. Và tôi cảm ơn bạn rất nhiều vì điều đó. Nhưng Alnitak cũng cung cấp những cách khác nhau và dường như cũng hoạt động. Vì tôi chỉ có thể chọn một câu trả lời, tôi sẽ chọn câu trả lời Alnitak. Không phải là giải pháp của anh ấy tốt hơn bạn hay đại loại như thế. Dù sao, tôi thực sự đánh giá cao giải pháp và nỗ lực của bạn.
Komaruloh

1
@Komaruloh: Ồ tôi nghĩ bạn luôn có thể bỏ phiếu trả lời cho câu hỏi của riêng bạn .... dù sao tôi cũng ít nhiều đùa, tôi không cần nhiều tiếng tăm hơn;) Chúc mừng mã hóa!
Felix Kling

1
@Felix Kling: Bạn cần ít nhất 15 danh tiếng để bỏ phiếu. :) Tôi tin rằng bạn không cần thêm danh tiếng với 69k +. Cảm ơn
Komaruloh

@Felix FWIW - chuyển đổi từ []cú pháp sang cú pháp thuộc tính là khá nhỏ.
Alnitak

4
Nếu bạn thay đổi vòng lặp while thành while (l > 0 && (obj = obj[current]) && i < l)thì mã này cũng hoạt động với các chuỗi không có dấu chấm.
Snea

39

Hoạt động cho các mảng / mảng bên trong đối tượng cũng. Phòng thủ chống lại các giá trị không hợp lệ.

/**
 * 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
Cảm ơn đây là câu trả lời hay nhất và hiệu quả nhất - jsfiddle.net/Jw8XB/1
Dominic

@Endless, tôi muốn nhấn mạnh đường dẫn nên phân tách các mục bằng dấu chấm. Niềng răng sẽ không hoạt động. Tức là để truy cập mục đầu tiên trong mảng sử dụng "0.sp ace".
TheZver

26

sử dụng eval:

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

bọc để trả lại không xác định lỗi

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

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

Vui lòng sử dụng thông thường và thận trọng khi sử dụng sức mạnh của eval. Nó hơi giống một thanh kiếm ánh sáng, nếu bạn bật nó lên, có 90% khả năng bạn sẽ cắt đứt chân tay. Nó không dành cho tất cả mọi người.


7
Việc eval có hay không là một ý tưởng tốt phụ thuộc vào việc dữ liệu chuỗi thuộc tính đến từ đâu. Tôi nghi ngờ bạn có bất kỳ lý do nào để lo ngại về việc tin tặc đột nhập thông qua một "var p = 'abc'; eval (p);" gõ cuộc gọi. Đó là một ý tưởng hoàn toàn tốt cho điều đó.
James Wilkins

17

Bạn có thể quản lý để có được giá trị của một thành viên đối tượng sâu với ký hiệu dấu chấm mà không cần bất kỳ thư viện JavaScript bên ngoài nào với thủ thuật đơn giản sau:

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

Trong trường hợp của bạn để có được giá trị part1.nametừ someObjectchỉ cần làm:

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

Đây là một bản demo fiddle đơn giản: https://jsfiddle.net/harishanchu/oq5esowf/


3
function deep_value (obj, path) {return new Hàm ('o', 'return o.' + path) (obj); }
ArcangelZith

14

Điều này có lẽ sẽ không bao giờ nhìn thấy ánh sáng trong ngày ... nhưng dù sao thì đây vẫn là.

  1. Thay thế [] cú pháp ngoặc bằng.
  2. Tách .nhân vật
  3. Xóa chuỗi trống
  4. Tìm đường dẫn (nếu không 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

Đó là một lớp lót với 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"

Hay thậm chí tốt hơn ...

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

Hoặc phiên bản ES6 w / giảm ...

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

Plunkr


7

Tôi nghĩ rằng bạn đang yêu cầu điều này:

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

Bạn có thể yêu cầu điều này:

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

Cả hai sẽ hoạt động


Hoặc có thể bạn đang yêu cầu điều này

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

var part1name = someObject[partName][nameStr];

Cuối cùng, bạn có thể yêu cầu điều này

var partName = "part1.name";

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

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

Tôi nghĩ OP đang yêu cầu giải pháp cuối cùng. Tuy nhiên, chuỗi không có Splitphương thức, nhưng đúng hơn split.
duri

Thực tế tôi đã hỏi người cuối cùng. Biến partName được điền với chuỗi biểu thị cấu trúc khóa thành giá trị. Giải pháp của bạn có vẻ có ý nghĩa. Tuy nhiên tôi có thể cần sửa đổi cho độ sâu mở rộng của dữ liệu, như mức 4-5 trở lên. Và tôi tự hỏi nếu tôi có thể xử lý các mảng và đối tượng thống nhất với điều này?
Komaruloh

7

Ở đây tôi cung cấp nhiều cách hơn, có vẻ nhanh hơn ở nhiều khía cạnh:

Tùy chọn 1: Chia chuỗi trên. hoặc [hoặc] hoặc 'hoặc ", đảo ngược nó, bỏ qua các mục trống.

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

Tùy chọn 2 (nhanh nhất trong tất cả, ngoại trừ eval): Quét ký tự cấp thấp (không có regex / split / etc, chỉ là quét char nhanh). Lưu ý: Điều này không hỗ trợ báo giá cho các chỉ mục.

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)

Tùy chọn 3: ( mới : tùy chọn 2 được mở rộng để hỗ trợ báo giá - chậm hơn một chút, nhưng vẫn nhanh)

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-deliated-property-opes/3

"Eval (...)" vẫn là vua mặc dù (đó là hiệu suất khôn ngoan). Nếu bạn có đường dẫn thuộc tính trực tiếp dưới sự kiểm soát của mình, sẽ không có bất kỳ vấn đề nào khi sử dụng 'eval' (đặc biệt là nếu muốn tốc độ). Nếu kéo các đường dẫn thuộc tính "qua dây" ( trên đường dây !? Lol: P), thì có, sử dụng một cái gì đó khác để an toàn. Chỉ có một thằng ngốc mới nói không bao giờ sử dụng "eval", vì có lý do chính đáng khi sử dụng nó. Ngoài ra, "Nó được sử dụng trong trình phân tích cú pháp JSON của Doug Crockford ." Nếu đầu vào là an toàn, thì không có vấn đề gì cả. Sử dụng đúng công cụ cho đúng công việc, đó là nó.


6

Chỉ trong trường hợp, bất kỳ ai truy cập câu hỏi này vào năm 2017 trở đi và tìm kiếm một cách dễ nhớ , đây là một bài đăng blog công phu về Truy cập các đối tượng lồng nhau trong JavaScript mà không bị xáo trộn bởi

Không thể đọc thuộc tính 'foo' của lỗi không xác định

Truy cập các đối tượng lồng nhau bằng cách sử dụng Array Giảm

Hãy lấy cấu trúc ví dụ này

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

Để có thể truy cập các mảng lồng nhau, bạn có thể viết mảng giảm của riêng bạn.

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.

Ngoài ra còn có một loại tuyệt vời xử lý typy thư viện tối thiểu mà làm tất cả điều này cho bạn.

Với typy, mã của bạn sẽ trông như thế này

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

Tuyên bố miễn trừ trách nhiệm: Tôi là tác giả của gói này.


6

AngularJS

Cách tiếp cận của Speigg rất gọn gàng và sạch sẽ, mặc dù tôi đã tìm thấy câu trả lời này trong khi tìm kiếm giải pháp truy cập các thuộc tính phạm vi AngularJS $ bằng đường dẫn chuỗi và với một chút sửa đổi, nó thực hiện công việc:

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

Chỉ cần đặt chức năng này trong bộ điều khiển gốc của bạn và sử dụng nó cho bất kỳ phạm vi con nào như thế này:

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

Xem AngularJS có$scope.$eval cách khác để làm điều đó với AngularJS.
georgeawg

3

Tôi chưa tìm thấy gói nào để thực hiện tất cả các thao tác với đường dẫn chuỗi, vì vậy cuối cùng tôi đã viết gói nhỏ nhanh của riêng mình hỗ trợ insert (), get () (với trả về mặc định), set () và remove ( ) hoạt động.

Bạn có thể sử dụng ký hiệu dấu chấm, dấu ngoặc, chỉ số số, thuộc tính số chuỗi và khóa với các ký tự không phải từ. Cách sử dụng đơn giản dưới đây:

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

https://github.com/vertical-ledgeledge/jsocud


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

Làm việc với

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

Hàm đơn giản, cho phép một đường dẫn chuỗi hoặc mảng.

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

HOẶC LÀ

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

Nếu bạn định đăng mã dưới dạng câu trả lời, vui lòng giải thích lý do tại sao mã trả lời câu hỏi.
Tieson T.


2

Mặc dù giảm là tốt, tôi ngạc nhiên không ai sử dụng forEach:

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

Kiểm tra


Bạn thậm chí không kiểm tra xem obj [key] có thực sự tồn tại không. Nó không đáng tin cậy.
Carles Alcolea

@CarlesAlcolea theo mặc định js sẽ không kiểm tra xem khóa của đối tượng có tồn tại hay không: a.b.csẽ đưa ra một ngoại lệ nếu không có bthuộc tính trong đối tượng của bạn. Nếu bạn cần một cái gì đó âm thầm loại bỏ các phím bấm sai (mà tôi không khuyến nghị), bạn vẫn có thể thay thế forEach bằng cái nàykeys.forEach((key)=> obj = (obj||{})[key]);
Flavien Volken 23/07/18

Tôi chạy qua nó một vật bị thiếu một cái nẹp xoăn, xấu của tôi :)
Carles Alcolea

1

Gần đây có cùng một câu hỏi và được sử dụng thành công https://npmjs.org/package/tea-properies cũng vậyset lồng đối tượng / mảng:

được:

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

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

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

bộ:

var o = {};

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

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

"Mô-đun này đã bị ngưng. Sử dụng chaijs / pathval."
Patrick Fisher

1

Lấy cảm hứng từ câu trả lời của @ webjay: https://stackoverflow.com/a/46008856/4110122

Tôi đã thực hiện chức năng này mà bạn có thể sử dụng nó để Nhận / Đặt / Bỏ đặt bất kỳ giá trị nào trong đối tượng

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

Để dùng nó:

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

Tôi đang phát triển cửa hàng trực tuyến với React. Tôi đã cố gắng thay đổi giá trị trong đối tượng trạng thái sao chép để cập nhật trạng thái ban đầu với nó khi gửi. Các ví dụ trên không có tác dụng với tôi, vì hầu hết chúng đều biến đổi cấu trúc của đối tượng sao chép. Tôi đã tìm thấy ví dụ hoạt động của hàm để truy cập và thay đổi giá trị của các thuộc tính đối tượng lồng nhau sâu: https://lowrey.me/create-an-object-by-path-in-javascript-2/ Đây là:

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

Dựa trên câu trả lời của Alnitak .

Tôi đã bọc polyfill trong một kiểm tra, và giảm chức năng xuống một chuỗi giảm duy nhất.

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

Nếu bạn cần truy cập khóa lồng nhau khác nhau mà không biết nó tại thời điểm mã hóa (sẽ rất đơn giản để giải quyết chúng), bạn có thể sử dụng trình truy cập ký hiệu mảng:

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

Chúng tương đương với trình truy cập ký hiệu dấu chấm và có thể thay đổi khi chạy, ví dụ:

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

var part1name = someObject[part][property];

tương đương với

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

hoặc là

var part1name = someObject.part1.name;

Tôi hy vọng địa chỉ này câu hỏi của bạn ...

BIÊN TẬP

Tôi sẽ không sử dụng một chuỗi để xác định một loại truy vấn xpath để truy cập một giá trị đối tượng. Khi bạn phải gọi một hàm để phân tích truy vấn và truy xuất giá trị, tôi sẽ đi theo một đường dẫn khác (không phải:

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

hoặc, nếu bạn không yên tâm với phương pháp áp dụng

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

Các chức năng ngắn hơn, rõ ràng hơn, trình thông dịch kiểm tra chúng cho bạn các lỗi cú pháp và vv.

Nhân tiện, tôi cảm thấy rằng một bài tập đơn giản được thực hiện đúng lúc sẽ có hiệu quả ...


Hấp dẫn. Nhưng trong trường hợp của tôi, someObject vẫn được khởi tạo khi tôi gán giá trị cho part1name. Tôi chỉ biết cấu trúc. Đó là lý do tại sao tôi sử dụng chuỗi để mô tả cấu trúc. Và hy vọng có thể sử dụng nó để truy vấn dữ liệu của tôi từ someObject. Cảm ơn đã chia sẻ suy nghĩ của bạn. :)
Komaruloh

@Komaruloh: Tôi nghĩ bạn sẽ viết rằng đối tượng KHÔNG được khởi tạo khi bạn tạo các biến của mình. Bằng cách này, tôi không nhận được điểm, tại sao bạn không thể thực hiện bài tập vào thời điểm thích hợp?
Eineki

Xin lỗi về việc không đề cập đến việc someObject chưa được khởi tạo. Về lý do, someObject được tìm nạp thông qua dịch vụ web. Và tôi muốn có một mảng tiêu đề bao gồm part1name, part2qty, v.v.
Komaruloh

0

Các giải pháp ở đây chỉ để truy cập vào các khóa được lồng sâu. Tôi cần một cái để truy cập, thêm, sửa đổi và xóa các phím. Đây là những gì tôi nghĩ ra:

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: đường dẫn trong một mảng. Bạn có thể thay thế nó bằng của bạn string_path.split(".").
  • type_of_function: 0 để truy cập (không chuyển bất kỳ giá trị nào value), 0 để thêm và sửa đổi. 1 để xóa.

0

Xây dựng câu trả lời của 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;
  };

}

Điều này cho phép bạn thiết lập một giá trị là tốt!

Tôi cũng đã tạo một gói npmgithub với điều này


0

Thay vì một chuỗi, một mảng có thể được sử dụng để thêm vào các đối tượng và mảng lồng nhau, ví dụ: ["my_field", "another_field", 0, "last_field", 10]

Dưới đây là một ví dụ sẽ thay đổi một trường dựa trên biểu diễn mảng này. Tôi đang sử dụng một cái gì đó tương tự trong Reac.js cho các trường đầu vào được kiểm soát thay đổi trạng thái của các cấu trúc lồng nhau.

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

Dựa trên câu trả lời trước đó, tôi đã tạo ra một hàm cũng có thể xử lý dấu ngoặc. Nhưng không có dấu chấm bên trong chúng do sự phân chia.

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

Làm việc với Underscore's propertyhoặc 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>

Chúc may mắn...

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.