Diberikan objek JS
var obj = { a: { b: '1', c: '2' } }
dan sebuah string
"a.b"
bagaimana saya bisa mengubah string ke notasi titik sehingga saya bisa pergi
var val = obj.a.b
Jika string itu adil 'a'
, saya bisa menggunakan obj[a]
. Tetapi ini lebih kompleks. Saya membayangkan ada beberapa metode sederhana tetapi lolos saat ini.
javascript
nevf
sumber
sumber
eval
itu jahat; jangan gunakan ituJawaban:
Berikut ini adalah one-liner elegan yang 10x lebih pendek dari solusi lain:
[Sunting] Atau dalam ECMAScript 6:
(Bukannya saya pikir eval selalu buruk seperti yang disarankan orang lain (meskipun biasanya demikian), namun orang-orang akan senang bahwa metode ini tidak menggunakan eval. Di atas akan ditemukan
obj.a.b.etc
diberikanobj
dan string"a.b.etc"
.)Menanggapi mereka yang masih takut menggunakan
reduce
meskipun berada dalam standar ECMA-262 (edisi ke-5), berikut adalah implementasi rekursif dua-baris:Bergantung pada optimisasi yang dilakukan oleh kompiler JS, Anda mungkin ingin memastikan bahwa fungsi yang disarangkan tidak didefinisikan ulang pada setiap panggilan melalui metode yang biasa (menempatkannya dalam penutupan, objek, atau namespace global).
edit :
Untuk menjawab pertanyaan yang menarik di komentar:
(sidenote: sayangnya tidak bisa mengembalikan objek dengan Setter, karena itu akan melanggar konvensi pemanggilan; komentator tampaknya merujuk pada fungsi gaya setter umum dengan efek samping seperti
index(obj,"a.b.etc", value)
melakukanobj.a.b.etc = value
.)The
reduce
Gaya ini tidak benar-benar cocok untuk itu, tapi kita bisa memodifikasi pelaksanaan rekursif:Demo:
... meskipun secara pribadi saya akan merekomendasikan membuat fungsi terpisah
setIndex(...)
. Saya ingin mengakhiri dengan catatan tambahan bahwa pelamar asli dari pertanyaan bisa (harus?) Bekerja dengan array indeks (yang dapat mereka peroleh.split
), daripada string; meskipun biasanya tidak ada yang salah dengan fungsi kenyamanan.Seorang komentator bertanya:
Javascript adalah bahasa yang sangat aneh; pada umumnya objek hanya dapat memiliki string sebagai kunci properti mereka, jadi misalnya jika
x
itu adalah objek generik sepertix={}
, makax[1]
akan menjadix["1"]
... Anda membacanya dengan benar ... ya ...Array Javascript (yang merupakan turunan dari Object) secara khusus mendorong kunci integer, meskipun Anda dapat melakukan sesuatu seperti
x=[]; x["puppy"]=5;
.Tetapi secara umum (dan ada pengecualian),
x["somestring"]===x.somestring
(ketika diizinkan; Anda tidak bisa melakukannyax.123
).(Perlu diingat bahwa kompiler JS apa pun yang Anda gunakan dapat memilih, mungkin, untuk mengkompilasi ini ke representasi yang lebih waras jika dapat membuktikannya tidak akan melanggar spesifikasi.)
Jadi jawaban untuk pertanyaan Anda akan tergantung pada apakah Anda mengasumsikan objek tersebut hanya menerima bilangan bulat (karena batasan dalam domain masalah Anda), atau tidak. Mari kita anggap tidak. Kemudian ekspresi yang valid adalah gabungan dari pengidentifikasi basis ditambah beberapa
.identifier
s ditambah beberapa["stringindex"]
sIni kemudian akan setara dengan
a["b"][4]["c"]["d"][1][2][3]
, meskipun kita mungkin juga harus mendukunga.b["c\"validjsstringliteral"][3]
. Anda harus memeriksa bagian tata bahasa ecmascript pada string literal untuk melihat cara mengurai string literal yang valid. Secara teknis Anda juga ingin memeriksa (tidak seperti dalam jawaban pertama saya) yanga
merupakan pengidentifikasi javascript yang valid .Namun, jawaban sederhana untuk pertanyaan Anda, jika string Anda tidak mengandung koma atau tanda kurung , akan sama dengan mencocokkan panjang 1+ urutan karakter yang tidak ada di set
,
atau[
atau]
:Jika string Anda tidak mengandung karakter melarikan diri atau
"
karakter , dan karena IdentifierNames adalah subbahasa dari StringLiterals (saya pikir ???), Anda dapat terlebih dahulu mengonversi titik Anda ke []:Tentu saja, selalu berhati-hati dan jangan pernah mempercayai data Anda. Beberapa cara buruk untuk melakukan ini yang mungkin berhasil untuk beberapa kasus penggunaan juga termasuk:
Sunting khusus 2018:
Mari kita mulai dan melakukan solusi terprogram yang paling tidak efisien, mengerikan, dan terlalu terprogram yang dapat kami buat ... untuk kepentingan
pemurnianpurtas sintaksis . Dengan objek Proxy ES6! ... Mari kita juga mendefinisikan beberapa properti yang (imho baik-baik saja tetapi luar biasa) dapat memecah pustaka yang ditulis secara tidak benar. Anda mungkin harus waspada menggunakan ini jika Anda peduli dengan kinerja, kewarasan (milik Anda atau orang lain), pekerjaan Anda, dll.Demo:
Keluaran:
ide yang tidak efisien: Anda dapat memodifikasi hal di atas untuk dikirim berdasarkan argumen input; baik menggunakan
.match(/[^\]\[.]+/g)
metode untuk mendukungobj['keys'].like[3]['this']
, atau jikainstanceof Array
, maka hanya menerima Array sebagai inputkeys = ['a','b','c']; obj.H[keys]
.Per saran yang mungkin Anda ingin menangani indeks tidak terdefinisi dengan gaya NaN 'lebih lembut' (mis.
index({a:{b:{c:...}}}, 'a.x.c')
Mengembalikan TypeError yang tidak terdefinisi daripada yang tidak tertangkap) ...::1) Ini masuk akal dari perspektif "kita harus mengembalikan undefined daripada melemparkan kesalahan" dalam situasi indeks 1-dimensi ({}) ['eg'] == undefined, jadi "kita harus mengembalikan undefined daripada melemparkan kesalahan "dalam situasi dimensi-N.
2) Hal ini tidak masuk akal dari perspektif yang kita lakukan
x['a']['x']['c']
, yang akan gagal dengan TypeError dalam contoh di atas.Yang mengatakan, Anda akan membuat pekerjaan ini dengan mengganti fungsi pengurangan Anda dengan:
(o,i)=>o===undefined?undefined:o[i]
, atau(o,i)=>(o||{})[i]
.(Anda dapat menjadikan ini lebih efisien dengan menggunakan for for dan break / return setiap kali subres yang akan Anda indeks selanjutnya tidak terdefinisi, atau menggunakan try-catch jika Anda berharap kegagalan seperti itu cukup langka.)
sumber
reduce
tidak didukung di semua browser yang saat ini digunakan.Array.reduce
adalah bagian dari standar ECMA-262. Jika Anda benar-benar ingin mendukung browser yang sudah ketinggalan zaman, Anda dapat menentukanArray.prototype.reduce
implementasi sampel yang diberikan di suatu tempat (misalnya 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 ); }
Jika Anda dapat menggunakan lodash , ada fungsi, yang melakukan hal itu:
_.get (objek, path, [defaultValue])
sumber
_.get(object, path)
tidak rusak jika jalan tidak ditemukan.'a.b.etc'.split('.').reduce((o,i)=>o[i], obj)
tidak. Untuk kasus khusus saya - bukan untuk setiap kasus - persis apa yang saya butuhkan. Terima kasih!defaultValue
. The_.get()
kembali metode nilai default jika_.get()
resolve keundefined
, jadi set ke apa pun yang Anda inginkan dan menonton untuk nilai yang ditetapkan._.set(object, path, value)
.Contoh yang sedikit lebih terlibat dengan rekursi.
sumber
Anda juga bisa menggunakan lodash.get
Anda cukup menginstal paket ini (npm i --simpan lodash.get) dan kemudian gunakan seperti ini:
sumber
Jika Anda berharap untuk mengulang jalur yang sama berkali-kali, membangun fungsi untuk setiap jalur notasi titik sebenarnya memiliki kinerja terbaik sejauh ini (memperluas tes perf yang dikaitkan dengan James Wilkins dalam komentar di atas).
Menggunakan Function constructor memiliki beberapa kelemahan yang sama dengan eval () dalam hal keamanan dan kinerja kasus terburuk, tetapi IMO itu adalah alat yang kurang dimanfaatkan untuk kasus-kasus di mana Anda memerlukan kombinasi dinamisme ekstrem dan kinerja tinggi. Saya menggunakan metodologi ini untuk membangun fungsi filter array dan memanggilnya di dalam loop intisari AngularJS. Profil saya secara konsisten menunjukkan langkah array.filter () yang membutuhkan kurang dari 1 ms untuk melakukan dereferensi dan memfilter sekitar 2000 objek kompleks, menggunakan jalur yang ditentukan secara dinamis dengan kedalaman 3-4 tingkat.
Metodologi serupa dapat digunakan untuk membuat fungsi setter, tentu saja:
sumber
Bertahun-tahun sejak posting asli. Sekarang ada perpustakaan hebat yang disebut 'object-path'. https://github.com/mariocasciaro/object-path
Tersedia di NPM dan BOWER https://www.npmjs.com/package/object-path
Semudah:
Dan berfungsi untuk properti dan array yang sangat bersarang.
sumber
Saya sarankan untuk membagi path dan iterate dan mengurangi objek yang Anda miliki. Proposal ini berfungsi dengan nilai default untuk properti yang hilang.
sumber
Catatan jika Anda sudah menggunakan Lodash, Anda dapat menggunakan
property
atauget
fungsinya:Garis bawah juga memiliki
property
fungsi tetapi tidak mendukung notasi titik.sumber
Proposal lain sedikit samar, jadi saya pikir saya akan berkontribusi:
sumber
Anda dapat menggunakan perpustakaan yang tersedia di npm, yang menyederhanakan proses ini. https://www.npmjs.com/package/dot-object
sumber
Ini banyak kode bila dibandingkan dengan cara yang lebih sederhana
eval
untuk melakukannya, tetapi seperti yang dikatakan Simon Willison, Anda sebaiknya tidak pernah menggunakan eval .Juga, JSFiddle .
sumber
Saya telah memperluas jawaban yang elegan dengan ninjagecko sehingga fungsinya menangani referensi gaya bertitik dan / atau array, dan sehingga string kosong menyebabkan objek induk dikembalikan.
Ini dia:
Lihat contoh jsFiddle kerja saya di sini: http://jsfiddle.net/sc0ttyd/q7zyd/
sumber
[]
notasi selalu untuk array. mungkin ada kunci objek yang diwakili seperti itu juga misalnyaobj['some-problem/name'].list[1]
Untuk memperbaikinya saya harus memperbaruiarr_deref
fungsi seperti inijavascript function arr_deref(o, ref, i) { return !ref ? o : (o[(ref.slice(0, i ? -1 : ref.length)).replace(/^['"]|['"]$/g, '')]); }
sumber
Anda dapat memperoleh nilai anggota objek dengan notasi titik dengan satu baris kode:
Jika Anda:
Untuk membuatnya sederhana, Anda dapat menulis fungsi seperti ini:
Penjelasan:
Konstruktor fungsi membuat objek fungsi baru. Dalam JavaScript setiap fungsi sebenarnya adalah objek Function. Sintaks untuk membuat fungsi secara eksplisit dengan Function constructor adalah:
di mana
arguments(arg1 to argN)
harus berupa string yang sesuai dengan pengidentifikasi javaScript yang valid danfunctionBody
merupakan string yang berisi pernyataan javaScript yang terdiri dari definisi fungsi.Dalam kasus kami, kami mengambil keuntungan dari fungsi string tubuh untuk mengambil anggota objek dengan notasi titik.
Semoga ini bisa membantu.
sumber
DAPATKAN / SET jawaban yang juga berfungsi dalam bahasa asli reaksi (Anda tidak dapat menetapkan untuk
Object.prototype
saat ini):Pemakaian:
sumber
Saya menyalin berikut dari jawaban Ricardo Tomasi dan dimodifikasi untuk juga membuat sub-objek yang belum ada seperlunya. Ini sedikit kurang efisien (lebih
if
dan menciptakan objek kosong), tetapi harus cukup bagus.Juga, itu akan memungkinkan kita untuk melakukan di
Object.prop(obj, 'a.b', false)
mana kita tidak bisa sebelumnya. Sayangnya, masih belum mengizinkan kami untuk menetapkanundefined
... Belum yakin bagaimana cara melakukannya.sumber
Jika Anda ingin mengonversi objek apa pun yang berisi kunci notasi titik menjadi versi yang tersusun dari kunci tersebut, Anda dapat menggunakan ini.
Ini akan mengonversi sesuatu seperti
untuk
sumber
Ini kode saya tanpa menggunakan
eval
. Mudah dimengerti juga.sumber
Ya, ditanya 4 tahun yang lalu dan ya, memperluas prototipe basis biasanya bukan ide yang baik, tetapi, jika Anda menyimpan semua ekstensi di satu tempat, mereka mungkin berguna.
Jadi, inilah cara saya untuk melakukan ini.
Sekarang Anda bisa mendapatkan properti bersarang di mana saja tanpa mengimpor modul dengan fungsi atau fungsi salin / tempel.
UPD. Contoh:
UPD 2. Perpanjangan berikutnya adalah luwak dalam proyek saya. Saya juga pernah membaca bahwa itu mungkin merusak jquery. Jadi, jangan pernah melakukannya dengan cara selanjutnya
sumber
Ini implementasi saya
Implementasi 1
Implementasi 2 (menggunakan pengurangan array bukan slice)
Contoh:
itu juga dapat menangani objek di dalam array untuk
sumber
Ini adalah solusi saya yang diusulkan oleh: ninjagecko
Bagi saya notasi string sederhana tidak cukup, jadi versi di bawah ini mendukung hal-hal seperti:
index (obj, 'data.accounts [0]. address [0] .postcode');
sumber
Dengan risiko mengalahkan kuda mati ... Saya menemukan ini paling berguna dalam melintasi objek bersarang untuk referensi di mana Anda berada sehubungan dengan objek dasar atau ke objek serupa dengan struktur yang sama. Untuk itu, ini berguna dengan fungsi traversal objek bersarang. Perhatikan bahwa saya telah menggunakan array untuk menahan path. Akan sepele untuk memodifikasi ini untuk menggunakan jalur string atau array. Perhatikan juga bahwa Anda dapat menetapkan "tidak terdefinisi" ke nilai, tidak seperti beberapa implementasi lainnya.
sumber
Saya menggunakan kode ini dalam proyek saya
Pemakaian:
sumber
Beberapa tahun kemudian, saya menemukan ini yang menangani lingkup dan array. misalnya
a['b']["c"].d.etc
sumber
Tidak jelas apa pertanyaan Anda. Mengingat objek Anda,
obj.a.b
akan memberi Anda "2" seperti apa adanya. Jika Anda ingin memanipulasi string untuk menggunakan tanda kurung, Anda bisa melakukan ini:sumber
a.b.c
, dan tidak benar-benar mencapai apa yang mereka inginkan .. Mereka menginginkan nilai, bukaneval
jalan.a.b.c
, tetapi Anda benar, tampaknya dia ingin mendapatkan / mengatur nilai propertiobj.a.b
. Pertanyaan itu membingungkan saya, karena dia mengatakan dia ingin "mengubah string" ....Inilah 10 sen saya untuk kasing, fungsi di bawah ini akan mendapatkan / set berdasarkan jalur yang disediakan, .. yakin Anda dapat memperbaikinya, hapus || dan menggantinya dengan
Object.hasOwnProperty
jika Anda benar-benar peduli dengan nilai-nilai yang salah,saya mengujinya dengan
a.b.c
dan ab2.c{a:{b:[0,1,{c:7}]}}
dan berfungsi untuk pengaturan dan mendapatkan :).bersorak
sumber