fungsi peta untuk objek (bukan array)

1067

Saya punya objek:

myObject = { 'a': 1, 'b': 2, 'c': 3 }

Saya mencari metode asli, mirip dengan Array.prototype.mapyang akan digunakan sebagai berikut:

newObject = myObject.map(function (value, label) {
    return value * value;
});

// newObject is now { 'a': 1, 'b': 4, 'c': 9 }

Apakah JavaScript memiliki mapfungsi untuk objek seperti itu? (Saya ingin ini untuk Node.JS, jadi saya tidak peduli tentang masalah lintas-browser.)

Randomblue
sumber
1
Sebagian besar jawaban digunakan Object.keys, yang tidak memiliki urutan yang jelas. Itu bisa bermasalah, saya sarankan menggunakan Object.getOwnPropertyNamessebagai gantinya.
Oriol
14
Luar biasa bagi saya bahwa JS tidak menyediakan tugas yang sangat sederhana ini.
jchook
3
@ Oriol, apakah Anda yakin tentang itu? Menurut dokumen web MDN, urutan elemen array konsisten antara Object.keysdan Object.getOwnPropertyNames. Lihat developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Bart
@Bart Urutan Object.keystergantung pada implementasi. Lihat stackoverflow.com/a/30919039/1529630
Oriol
4
@Oriol Anda tidak seharusnya bergantung pada urutan kunci pada objek, jadi itu agak tidak relevan dengan pertanyaan - karena dia tidak pernah menentukan bahwa urutan itu penting baginya. Dan jika pesanan penting baginya, dia seharusnya tidak menggunakan objek sama sekali.
BT

Jawaban:

1545

Tidak ada yang asli mappada Objectobjek, tetapi bagaimana dengan ini:

var myObject = { 'a': 1, 'b': 2, 'c': 3 };

Object.keys(myObject).map(function(key, index) {
  myObject[key] *= 2;
});

console.log(myObject);
// => { 'a': 2, 'b': 4, 'c': 6 }

Tapi Anda bisa dengan mudah beralih pada objek menggunakan for ... in:

var myObject = { 'a': 1, 'b': 2, 'c': 3 };

for (var key in myObject) {
  if (myObject.hasOwnProperty(key)) {
    myObject[key] *= 2;
  }
}

console.log(myObject);
// { 'a': 2, 'b': 4, 'c': 6 }

Memperbarui

Banyak orang menyebutkan bahwa metode sebelumnya tidak mengembalikan objek baru, tetapi beroperasi pada objek itu sendiri. Untuk itu saya ingin menambahkan solusi lain yang mengembalikan objek baru dan meninggalkan objek aslinya seperti:

var myObject = { 'a': 1, 'b': 2, 'c': 3 };

// returns a new object with the values at each key mapped using mapFn(value)
function objectMap(object, mapFn) {
  return Object.keys(object).reduce(function(result, key) {
    result[key] = mapFn(object[key])
    return result
  }, {})
}

var newObject = objectMap(myObject, function(value) {
  return value * 2
})

console.log(newObject);
// => { 'a': 2, 'b': 4, 'c': 6 }

console.log(myObject);
// => { 'a': 1, 'b': 2, 'c': 3 }

Array.prototype.reducemengurangi array ke nilai tunggal dengan cara menggabungkan nilai sebelumnya dengan arus. Rantai diinisialisasi oleh objek kosong {}. Pada setiap iterasi kunci baru myObjectditambahkan dengan dua kali kunci sebagai nilainya.

Memperbarui

Dengan fitur ES6 baru, ada cara yang lebih elegan untuk berekspresi objectMap.

const objectMap = (obj, fn) =>
  Object.fromEntries(
    Object.entries(obj).map(
      ([k, v], i) => [k, fn(v, k, i)]
    )
  )
  
const myObject = { a: 1, b: 2, c: 3 }

console.log(objectMap(myObject, v => 2 * v)) 

Amberlamps
sumber
3
Ya, saya tidak akan merekomendasikan solusi saya untuk masalah seperti itu. Itulah sebabnya saya memperbarui jawaban saya dengan solusi alternatif yang lebih baik.
Amberlamps
1
Saya telah memperbarui jawaban saya - sepertinya (saat membaca kembali pertanyaan) bahwa OP menginginkan objek baru dengan kunci yang sama, di mana nilainya dimutasi dengan fungsi yang disediakan, dan bukan untuk memodifikasi objek asli di tempat.
Alnitak
13
@ KalhalRavanna Saya pikir Anda salah membaca kode di sini - jawaban ini tidak digunakan mapdengan benar karena tidak melakukan return- itu menyalahgunakan mapseolah-olah itu forEachpanggilan. Jika dia benar - benar melakukannyareturn myObject[value] * 2 maka hasilnya akan menjadi array yang berisi nilai-nilai asli dua kali lipat, bukan objek yang berisi kunci asli dengan nilai-nilai dua kali lipat, yang terakhir jelas menjadi apa yang diminta OP.
Alnitak
3
@Amberlamps .reducecontoh Anda OK, tapi IMHO masih jauh dari kenyamanan .mapuntuk Objek, karena .reducepanggilan balik Anda tidak hanya harus cocok dengan cara yang .reducebekerja, tetapi juga mengharuskan itu myObjecttersedia dalam lingkup leksikal. Yang terakhir khususnya membuatnya tidak mungkin untuk hanya melewati referensi fungsi di callback, membutuhkan fungsi anonim di tempat sebagai gantinya.
Alnitak
8
TBH Saya akan lebih memilih jawaban yang hanya (atau awalnya) memberikan solusi kedua yang disajikan dalam jawaban ini. Yang pertama berfungsi dan tidak selalu salah, tetapi seperti yang dinyatakan orang lain, saya tidak suka melihat peta digunakan seperti itu. Ketika saya melihat peta, pikiran saya secara otomatis berpikir "struktur data tidak berubah"
mjohnsonengr
308

Bagaimana dengan satu liner dengan penugasan variabel langsung di JS polos ( ES6 / ES2015 )?

Memanfaatkan operator spread dan sintaks nama kunci yang dikomputasi :

let newObj = Object.assign({}, ...Object.keys(obj).map(k => ({[k]: obj[k] * obj[k]})));

jsbin

Versi lain menggunakan pengurangan:

let newObj = Object.keys(obj).reduce((p, c) => ({...p, [c]: obj[c] * obj[c]}), {});

jsbin

Contoh pertama sebagai fungsi:

const oMap = (o, f) => Object.assign({}, ...Object.keys(o).map(k => ({ [k]: f(o[k]) })));

// To square each value you can call it like this:
let mappedObj = oMap(myObj, (x) => x * x);

jsbin

Jika Anda ingin memetakan objek bersarang secara rekursif dalam gaya fungsional , itu bisa dilakukan seperti ini:

const sqrObjRecursive = obj =>
  Object.keys(obj).reduce(
    (newObj, key) =>
      obj[key] && typeof obj[key] === "object"
        ? { ...newObj, [key]: sqrObjRecursive(obj[key]) } // recurse.
        : { ...newObj, [key]: obj[key] * obj[key] }, // square val.
    {}
  );       

jsbin

Atau lebih penting lagi, seperti ini:

const sqrObjRecursive = obj => {
  Object.keys(obj).forEach(key => {
    if (typeof obj[key] === "object") obj[key] = sqrObjRecursive(obj[key]);
    else obj[key] = obj[key] * obj[key];
  });
  return obj;
};

jsbin

Karena ES7 / ES2016 Anda dapat menggunakan, Object.entries()bukan Object.keys()misalnya, seperti ini:

let newObj = Object.assign({}, ...Object.entries(obj).map(([k, v]) => ({[k]: v * v})));

ES2019 diperkenalkan Object.fromEntries(), yang semakin menyederhanakan ini:

let newObj = Object.fromEntries(Object.entries(obj).map(([k, v]) => [k, v * v]));


Properti yang diwariskan dan rantai prototipe:

Dalam beberapa situasi yang jarang terjadi, Anda mungkin perlu memetakan objek seperti kelas yang menyimpan properti dari objek yang diwarisi pada rantai prototipe-nya . Dalam kasus seperti itu Object.keys()tidak akan berfungsi, karena Object.keys()tidak menyebutkan properti yang diwarisi . Jika Anda perlu memetakan properti yang diwarisi , Anda harus menggunakanfor (key in myObj) {...} .

Berikut adalah contoh dari objek yang mewarisi properti dari objek lain dan bagaimana Object.keys()tidak bekerja dalam skenario seperti itu.

const obj1 = { 'a': 1, 'b': 2, 'c': 3}
const obj2 = Object.create(obj1);  // One of multiple ways to inherit an object in JS.

// Here you see how the properties of obj1 sit on the 'prototype' of obj2
console.log(obj2)  // Prints: obj2.__proto__ = { 'a': 1, 'b': 2, 'c': 3}

console.log(Object.keys(obj2));  // Prints: an empty Array.

for (key in obj2) {
  console.log(key);              // Prints: 'a', 'b', 'c'
}

jsbin

Namun, tolong bantu saya dan hindari warisan . :-)

Rotareti
sumber
1
Cantik tapi saya tertangkap oleh fakta yang Object.keystidak menyebutkan properti warisan. Saya sarankan Anda menambahkan peringatan.
David Braun
Untuk mempersingkat jawaban Anda, gunakan saja Object.entries({a: 1, b: 2, c: 3}).
1
Anda memiliki beberapa kesalahan ketik (Anda menyebutnya (o, f)sebagai argumen tetapi digunakan objdalam tubuh.
kzahel
4
Ya dan kemudian biarkan orang berikutnya menghabiskan beberapa menit mencari tahu apa fungsi ini? :)
Andrey Popov
1
Solusinya Object.assign(...Object.entries(obj).map(([k, v]) => ({[k]: v * v})))tidak berfungsi jika objbenda kosong. Ubah ke: Object.assign({}, ...Object.entries(obj).map(([k, v]) => ({[k]: v * v}))).
blackcatweb
117

Tidak ada metode asli, tetapi lodash # mapValues akan melakukan pekerjaan dengan cemerlang

_.mapValues({ 'a': 1, 'b': 2, 'c': 3} , function(num) { return num * 3; });
// → { 'a': 3, 'b': 6, 'c': 9 }
Mario
sumber
6
Tidak perlu menambahkan panggilan HTTP tambahan dan perpustakaan tambahan hanya untuk satu fungsi. Bagaimanapun, jawaban ini sekarang sudah usang dan Anda dapat menelepon Object.entries({a: 1, b: 2, c: 3})untuk mendapatkan array.
11
Mengapa mendapatkan array bermanfaat? Persyaratan adalah untuk objek yang dipetakan. Juga tidak ada panggilan HTTP tersirat dalam menggunakan Lodash, sebuah perpustakaan populer yang pada dasarnya dibuat untuk jenis hal ini, meskipun ada beberapa hal yang ES6 telah menghalangi perlunya menggunakan fungsi perpustakaan fungsi untuk beberapa derajat belakangan ini.
corse32
2
panggilan HTTP tambahan untuk menarik Lodash, kurasa. Jika ini adalah satu-satunya kasus penggunaan Anda, memang itu adalah pembunuhan yang berlebihan. Meskipun demikian, masuk akal untuk memiliki jawaban ini karena Lodash adalah perpustakaan yang tersebar luas.
igorsantos07
2
Belum lagi Anda harus benar-benar menggunakan _.map()sehingga Anda mendapatkan kunci sebagai argumen kedua - seperti yang disyaratkan oleh OP.
igorsantos07
_.mapValues()tidak juga menyediakan kunci sebagai argumen ke-2. Juga _.map()tidak akan bekerja di sini karena hanya mengembalikan array, bukan objek:_.map({ 'a': 1, 'b': 2, 'c': 3}, n => n * 3) // [3, 6, 9]
MeltedPenguin
57

Cukup mudah untuk menulis satu:

Object.map = function(o, f, ctx) {
    ctx = ctx || this;
    var result = {};
    Object.keys(o).forEach(function(k) {
        result[k] = f.call(ctx, o[k], k, o); 
    });
    return result;
}

dengan kode contoh:

> o = { a: 1, b: 2, c: 3 };
> r = Object.map(o, function(v, k, o) {
     return v * v;
  });
> r
{ a : 1, b: 4, c: 9 }

NB: versi ini juga memungkinkan Anda untuk (secara opsional) mengatur thiskonteks panggilan balik, sama seperti Arraymetode.

EDIT - diubah untuk menghapus penggunaan Object.prototype, untuk memastikan tidak berbenturan dengan properti yang ada yang disebutkan mappada objek.

Alnitak
sumber
3
Baik. :) Komentar untuk pertanyaan ini membawaku ke sini. Saya akan memberi ini +1 karena jawaban yang diterima jelas menyalahgunakan map, tetapi saya harus mengatakan bahwa memodifikasi Object.prototypetidak cocok dengan saya, bahkan jika itu tidak dapat dihitung.
JLRishe
2
@JLRishe Terima kasih. IMHO di ES5 benar-benar tidak ada lagi alasan untuk tidak memodifikasi Object.prototypeselain (secara teoritis) risiko tabrakan dengan metode lain. FWIW, saya tidak bisa mengerti bagaimana jawaban yang lain mendapat suara sebanyak itu, itu benar-benar salah.
Alnitak
7
@ JLRishe kau kakek tua ... dapatkan dengan waktu dan memodifikasi prototipe itu!
Nick Manning
12
@NickManning Anda pembuat whippingnapp muda dan perilaku tidak bertanggung jawab Anda. Turun dari halaman saya!
JLRishe
3
Ini sudah tidak bekerja: o = {map: true, image: false}. Anda mempertaruhkannya hanya dengan menulis o.map(fn)alih-alihmap(o,fn)
fregante
25

Anda bisa menggunakan Object.keysdan kemudian forEachmelewati susunan kunci yang dikembalikan:

var myObject = { 'a': 1, 'b': 2, 'c': 3 },
    newObject = {};
Object.keys(myObject).forEach(function (key) {
    var value = myObject[key];
    newObject[key] = value * value;
});

Atau dengan cara yang lebih modular:

function map(obj, callback) {
    var result = {};
    Object.keys(obj).forEach(function (key) {
        result[key] = callback.call(obj, obj[key], key, obj);
    });
    return result;
}

newObject = map(myObject, function(x) { return x * x; });

Perhatikan bahwa Object.keysmengembalikan array yang hanya berisi properti enumerable objek sendiri, sehingga berperilaku seperti for..inloop dengan tanda hasOwnPropertycentang.

Mattias Buelens
sumber
20

Ini lurus bs, dan semua orang di komunitas JS tahu itu. Ada harus menjadi fungsi ini:

const obj1 = {a:4, b:7};
const obj2 = Object.map(obj1, (k,v) => v + 5);

console.log(obj1); // {a:4, b:7}
console.log(obj2); // {a:9, b:12}

di sini adalah implementasi naif:

Object.map = function(obj, fn, ctx){

    const ret = {};

    for(let k of Object.keys(obj)){
        ret[k] = fn.call(ctx || null, k, obj[k]);
    });

    return ret;
};

itu sangat menjengkelkan harus mengimplementasikan ini sendiri setiap saat;)

Jika Anda menginginkan sesuatu yang sedikit lebih canggih, yang tidak mengganggu kelas Object, coba ini:

let map = function (obj, fn, ctx) {
  return Object.keys(obj).reduce((a, b) => {
    a[b] = fn.call(ctx || null, b, obj[b]);
    return a;
  }, {});
};


const x = map({a: 2, b: 4}, (k,v) => {
    return v*2;
});

tetapi aman untuk menambahkan fungsi peta ini ke Object, tapi jangan menambahkan Object.prototype.

Object.map = ... // fairly safe
Object.prototype.map ... // not ok
Alexander Mills
sumber
Mengapa Anda menambahkan fungsi ini ke Objectobjek global ? Hanya punya const mapObject = (obj, fn) => { [...]; return ret; }.
Amberlamps
7
Karena itu harus menjadi metode pada Object :)
Alexander Mills
1
Selama kita tidak main-main dengan Object.prototype maka kita harus baik-baik saja
Alexander Mills
1
Jika peta hanya hal-hal yang samar-samar acak, tentu saja. Tetapi .mapsecara umum dipahami sebagai antarmuka Functor, dan tidak ada definisi tunggal Functor untuk "Objek" karena hampir semua hal dalam javascript dapat menjadi Obyek, termasuk hal-hal dengan antarmuka / logika .map / logika.
Dtipson
1
Menurut pendapat saya argumen pertama dari callback harus menjadi elemen yang berulang itu sendiri, bukan kunci. Misalnya saya menganggap itu harus memberi saya objek yang sama, seperti biasanya terjadi dengan peta umum: Object.map ({prop: 1}, el => el) tetapi mengembalikan kunci dengan nilai nama kunci yang sama ...
Gleb Dolzikov
17

Saya datang ke sini mencari untuk menemukan dan menjawab untuk memetakan objek ke array dan mendapatkan halaman ini sebagai hasilnya. Jika Anda datang ke sini mencari jawaban yang sama dengan saya, berikut adalah cara Anda dapat memetakan dan menolak array.

Anda dapat menggunakan peta untuk mengembalikan array baru dari objek seperti:

var newObject = Object.keys(myObject).map(function(key) {
   return myObject[key];
});
JoeTron
sumber
6
Ini tidak bekerja dari jauh - hanya mengembalikan array nilai-nilai objek, bukan objek baru. Saya tidak percaya ini memiliki banyak suara.
Alnitak
1
Anda benar, itu tidak menjawab pertanyaan dengan benar karena mereka mencari jawaban untuk memetakan objek dan objek. Saya datang ke sini mencari jawaban untuk memetakan objek ke array dan mendapatkan halaman ini sebagai hasilnya, jadi saya akan meninggalkan jawaban saya dan mengeditnya untuk mencerminkan bahwa itu adalah jawaban alternatif untuk orang-orang yang mungkin sudah ada di sini sebagai akibat dari mencari pemetaan objek ke array.
JoeTron
5
Dalam ES7 itu akan menjadi sepeleObject.values(myObject)
Alnitak
1
Itu bagus untuk diketahui, tetapi beberapa orang masih harus menggunakan kode warisan.
JoeTron
const obj = {foo: 'bar', baz: 42}; console.log (Object.entries (obj)); // [['foo', 'bar'], ['baz', 42]]
Bashirpour
12

Jawaban yang diterima memiliki dua kelemahan:

  • Menyalahgunakan Array.prototype.reduce, karena mengurangi berarti mengubah struktur tipe komposit, yang tidak terjadi dalam kasus ini.
  • Ini tidak dapat digunakan kembali secara khusus

Pendekatan fungsional ES6 / ES2015

Harap dicatat bahwa semua fungsi didefinisikan dalam bentuk kari.

// small, reusable auxiliary functions

const keys = o => Object.keys(o);

const assign = (...o) => Object.assign({}, ...o);

const map = f => xs => xs.map(x => f(x));

const mul = y => x => x * y;

const sqr = x => mul(x) (x);


// the actual map function

const omap = f => o => {
  o = assign(o); // A
  map(x => o[x] = f(o[x])) (keys(o)); // B
  return o;
};


// mock data

const o = {"a":1, "b":2, "c":3};


// and run

console.log(omap(sqr) (o));
console.log(omap(mul(10)) (o));

  • Di baris A oditugaskan kembali. Karena Javascript melewati nilai referensi dengan berbagi , salinan dangkal odihasilkan. Kita sekarang dapat bermutasi di odalam omaptanpa bermutasio dalam lingkup induk.
  • Pada baris B mapnilai pengembalian diabaikan, karena mapmelakukan mutasi o. Karena efek samping ini tetap ada di dalam omapdan tidak terlihat dalam lingkup induk, itu sepenuhnya dapat diterima.

Ini bukan solusi tercepat, tetapi yang deklaratif dan dapat digunakan kembali. Berikut ini adalah implementasi yang sama dengan satu baris, ringkas tapi kurang mudah dibaca:

const omap = f => o => (o = assign(o), map(x => o[x] = f(o[x])) (keys(o)), o);

Tambahan - mengapa objek tidak dapat diubah secara default?

ES2015 menetapkan protokol iterator dan iterable. Tetapi benda-benda masih tidak bisa diubah dan karenanya tidak bisa dipetakan. Alasannya adalah pencampuran data dan tingkat program .

Komunitas
sumber
1
Sepertinya sedikit over engineered; Saya menghitung 6 fungsi untuk apa dasarnya loop. Solusi lain jauh lebih sederhana dan jawaban yang diterima juga 5 kali lebih cepat.
fregante
4
@ bfred.it Apa tujuan dari komentar Anda? Jika Anda menyukai pengoptimalan mikro, Anda sebaiknya tidak menggunakan Array.prototype.reducekeduanya. Yang ingin saya ilustrasikan di sini adalah bahwa jawaban yang diterima entah bagaimana "menyalahgunakan" Array.prototype.reducedan bagaimana pemetaan yang murni fungsional dapat diimplementasikan. Jika Anda tidak tertarik pada pemrograman fungsional, abaikan saja jawaban saya.
2
"Mengurangi berarti mengubah struktur tipe komposit" Saya tidak berpikir itu benar. Array.reduce yang menghasilkan array baru dengan panjang yang sama persis atau bahkan nilai yang sama persis adalah sah. Reduce dapat digunakan untuk membuat kembali fungsionalitas peta dan / atau filter (atau keduanya, seperti pada transduser yang kompleks, yang mana reduksi adalah basisnya). Dalam FP, itu adalah jenis lipatan, dan lipatan yang berakhir dengan jenis yang sama masih lipatan.
Dtipson
2
@Dipson Anda benar sekali. A foldlebih umum daripada map(functor). Namun, ini adalah pengetahuan saya dari pertengahan 2016. Itu setengah keabadian: D
1
Ha, ya. Butuh waktu bertahun-tahun bagi saya untuk menyadari betapa saling terkait semua hal ini, dan ekosistem istilah dan antarmuka yang lebih luas. Hal-hal yang kelihatannya begitu mudah ternyata menjadi sangat rumit, dan beberapa hal yang tampaknya abstrak sangat rumit pergi dengan sangat rapi. :)
Dtipson
10

JavaScript baru saja mendapatkan Object.fromEntriesmetode baru .

Contoh

function mapObject (obj, fn) {
  return Object.fromEntries(
    Object
      .entries(obj)
      .map(fn)
  )
}

const myObject = { a: 1, b: 2, c: 3 }
const myNewObject = mapObject(myObject, ([key, value]) => ([key, value * value]))
console.log(myNewObject)

Penjelasan

Kode di atas mengubah Objek menjadi Array bersarang ( [[<key>,<value>], ...]) yang dapat Anda petakan. Object.fromEntriesmengubah Array kembali ke Object.

Yang keren tentang pola ini, adalah bahwa Anda sekarang dapat dengan mudah mengambil kunci objek ke akun saat memetakan.

Dokumentasi

Dukungan Browser

Object.fromEntriessaat ini hanya didukung oleh peramban / mesin ini , namun ada polyfill yang tersedia (mis. @ babel / polyfill ).

HaNdTriX
sumber
2
Pada dasarnya, Object.fromEntrieskebalikan dari Object.entries. Object.entriesmengonversi objek ke daftar pasangan nilai kunci. Object.fromEntriesmengonversi daftar pasangan nilai kunci ke objek.
orad
8

Versi minimal (es6):

Object.entries(obj).reduce((a, [k, v]) => (a[k] = v * v, a), {})
Yukulélé
sumber
Saya pikir ini salah karena kesalahan ketik, saya pikir maksud Anda => Object.entries(obj).reduce((a, [k, v]) => [a[k] = v * v, a], {}), kurung-siku bukan tanda kurung.
Alexander Mills
@AlexanderMills, kode saya sudah benar. Baca lebih lanjut tentang operator koma di dalam tanda kurung: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Yukulélé
mengerti, Anda mungkin ingin menjelaskannya lain kali, tapi saya kira itulah gunanya komentar.
Alexander Mills
1
pada dasarnya, kode saya setara dengan:Object.entries(obj).reduce((a, [k, v]) => {a[k] = v * v; return a}, {})
Yukulélé
1
Jawaban yang bagus Object.entries().reduceadalah solusi terbaik yang saya pikir dengan ES6 +. Akan mendapatkan lebih banyak upvotes jika menggunakan returnpernyataan dalam peredam daripada implicit returndan commaoperator - yang rapi tapi sulit dibaca IMHO
Drenai
7

Untuk kinerja maksimal.

Jika objek Anda tidak sering berubah tetapi perlu diulangi sering saya sarankan menggunakan Peta asli sebagai cache.

// example object
var obj = {a: 1, b: 2, c: 'something'};

// caching map
var objMap = new Map(Object.entries(obj));

// fast iteration on Map object
objMap.forEach((item, key) => {
  // do something with an item
  console.log(key, item);
});

Object.entries sudah berfungsi di Chrome, Edge, Firefox dan beta Opera jadi ini fitur yang tahan masa depan. Ini dari ES7 jadi polyfill https://github.com/es-shims/Object.entries untuk IE yang tidak berfungsi.

Pawel
sumber
Bagaimana dengan menggunakan Destructing ?
Константин Ван
1
@ K. dulu sangat lambat, tapi saya tidak tahu tentang kinerja sekarang. Sepertinya V8 v 5.6 memperkenalkan beberapa optimisasi untuk penghancuran tetapi itu harus diukur v8project.blogspot.co.uk
Pawel
Atau, es7 yang lebih sedikit performanya tetapi tidak berubah:const fn = v => v * 2; const newObj = Object.entries(myObject).reduce((acc, [k,v]) => Object.assign({}, acc, {[k]: fn(v)}), {});
mikebridge
Saya bertanya-tanya mengapa tidak ada jawaban lain yang menggunakan Object.entries. Ini jauh lebih bersih daripada beralih pada kunci dan menggunakan obj [kunci].
corwin.amber
6

Anda dapat menggunakan mapmetode dan forEachpada array tetapi jika Anda ingin menggunakannya Objectmaka Anda dapat menggunakannya dengan twist seperti di bawah ini:

Menggunakan Javascript (ES6)

var obj = { 'a': 2, 'b': 4, 'c': 6 };   
Object.entries(obj).map( v => obj[v[0]] *= v[1] );
console.log(obj); //it will log as {a: 4, b: 16, c: 36}

var obj2 = { 'a': 4, 'b': 8, 'c': 10 };
Object.entries(obj2).forEach( v => obj2[v[0]] *= v[1] );
console.log(obj2); //it will log as {a: 16, b: 64, c: 100}

Menggunakan jQuery

var ob = { 'a': 2, 'b': 4, 'c': 6 };
$.map(ob, function (val, key) {
   ob[key] *= val;
});
console.log(ob) //it will log as {a: 4, b: 16, c: 36}

Atau Anda dapat menggunakan loop lain juga seperti $.eachmetode seperti contoh di bawah ini:

$.each(ob,function (key, value) {
  ob[key] *= value;
});
console.log(ob) //it will also log as {a: 4, b: 16, c: 36}
Haritsinh Gohil
sumber
Di mana $jquery?
masterxilo
Ya, Anda dapat menggunakan $ dan jQuery
Haritsinh Gohil
1
@Ronald saya salah paham, lihat jawaban saya yang diperbarui, terima kasih karena tidak downvoting tetapi benar-benar menyadari kesalahan saya, terima kasih telah meningkatkan komunitas.
Haritsinh Gohil
@bcoughlan ya itu persis yang saya tulis dalam komentar juga, itu adalah kuadrat angka, apa yang Anda inginkan?
Haritsinh Gohil
1
@ HaritsinhGohil Saya menganggapnya berarti dia mengkritik Anda karena memberikan solusi jQuery. Memang benar bahwa ada beberapa dorongan hari ini untuk menjauh dari jQuery, dan tentu saja ada Node, tetapi kecuali OP meminta secara khusus untuk JS atau Node murni, saya pikir tidak apa-apa untuk menganggap seseorang bekerja di lingkungan web dan jQuery itu hampir pasti akan tersedia.
abalter
4

Tidak map functionada pada Object.prototypenamun Anda dapat meniru seperti itu

var myMap = function ( obj, callback ) {

    var result = {};

    for ( var key in obj ) {
        if ( Object.prototype.hasOwnProperty.call( obj, key ) ) {
            if ( typeof callback === 'function' ) {
                result[ key ] = callback.call( obj, obj[ key ], key, obj );
            }
        }
    }

    return result;

};

var myObject = { 'a': 1, 'b': 2, 'c': 3 };

var newObject = myMap( myObject, function ( value, key ) {
    return value * value;
});
whitneyit
sumber
Masalah kinerja: typeof callback === 'function'benar-benar berlebihan. 1) maptidak memiliki arti tanpa panggilan balik 2) jika callbacktidak berfungsi, mapimplementasi Anda hanya berjalan tanpa makna untuk-loop. Juga, Object.prototype.hasOwnProperty.call( obj, key )sedikit berlebihan.
ankhzet
3

Saya menemukan ini sebagai item pertama dalam pencarian Google yang mencoba belajar untuk melakukan ini, dan saya pikir saya akan berbagi untuk folsk lain menemukan ini baru-baru ini solusi yang saya temukan, yang menggunakan paket npm tidak dapat diubah.

Saya pikir ini menarik untuk dibagikan karena immutable menggunakan situasi OP EXACT dalam dokumentasi mereka sendiri - berikut ini bukan kode saya sendiri tetapi ditarik dari dokumentasi immutable-js saat ini:

const { Seq } = require('immutable')
const myObject = { a: 1, b: 2, c: 3 }
Seq(myObject).map(x => x * x).toObject();
// { a: 1, b: 4, c: 9 } 

Bukan berarti Seq memiliki properti lain ("Seq menggambarkan operasi malas, memungkinkan mereka untuk secara efisien menggunakan semua metode pengumpulan tingkat tinggi (seperti peta dan filter) dengan tidak membuat koleksi perantara") dan bahwa beberapa data js lainnya yang tidak dapat diubah struktur mungkin juga melakukan pekerjaan dengan cukup efisien.

Siapa pun yang menggunakan metode ini tentu saja harus npm install immutabledan mungkin ingin membaca dokumen:

https://facebook.github.io/immutable-js/

alex.h
sumber
3

EDIT: Cara kanonik menggunakan fitur JavaScript yang lebih baru adalah -

const identity = x =>
  x

const omap = (f = identity, o = {}) =>
  Object.fromEntries(
    Object.entries(o).map(([ k, v ]) =>
      [ k, f(v) ]
    )
  )

Di mana obeberapa objek dan ffungsi pemetaan Anda. Atau kita dapat mengatakan, diberi fungsi dari a -> b, dan objek dengan nilai tipe a, menghasilkan objek dengan nilai tipe b. Sebagai tanda tangan tipe semu -

// omap : (a -> b, { a }) -> { b }

Jawaban aslinya ditulis untuk menunjukkan kombinasi yang kuat, mapReduceyang memungkinkan kita untuk memikirkan transformasi kita dengan cara yang berbeda

  1. m, fungsi pemetaan - memberi Anda kesempatan untuk mengubah elemen yang masuk sebelum ...
  2. r, fungsi pereduksi - fungsi ini menggabungkan akumulator dengan hasil elemen yang dipetakan

Secara intuitif, mapReducebuat peredam baru yang bisa kita pasang langsung Array.prototype.reduce. Tetapi yang lebih penting, kita dapat mengimplementasikan implementasi functor objek kita secara omapjelas dengan memanfaatkan objek monoid, Object.assigndan {}.

const identity = x =>
  x
  
const mapReduce = (m, r) =>
  (a, x) => r (a, m (x))

const omap = (f = identity, o = {}) =>
  Object
    .keys (o)
    .reduce
      ( mapReduce
          ( k => ({ [k]: f (o[k]) })
          , Object.assign
          )
      , {}
      )
          
const square = x =>
  x * x
  
const data =
  { a : 1, b : 2, c : 3 }
  
console .log (omap (square, data))
// { a : 1, b : 4, c : 9 }

Perhatikan bahwa satu-satunya bagian dari program yang harus kami tulis adalah implementasi pemetaan itu sendiri -

k => ({ [k]: f (o[k]) })

Yang mengatakan, diberi objek yang diketahui odan beberapa kunci k, membangun objek dan yang properti yang dihitung kadalah hasil dari memanggil fnilai kunci o[k],.

Kita mendapatkan sekilas mapReducepotensi sequencing jika kita abstrak terlebih dahuluoreduce

// oreduce : (string * a -> string * b, b, { a }) -> { b }
const oreduce = (f = identity, r = null, o = {}) =>
  Object
    .keys (o)
    .reduce
      ( mapReduce
          ( k => [ k, o[k] ]
          , f
          )
      , r
      )

// omap : (a -> b, {a}) -> {b}
const omap = (f = identity, o = {}) =>
  oreduce
    ( mapReduce
        ( ([ k, v ]) =>
            ({ [k]: f (v) })
        , Object.assign
        )
    , {}
    , o
    )

Semuanya berfungsi sama, tetapi sekarang omapdapat didefinisikan pada tingkat yang lebih tinggi. Tentu saja yang baru Object.entriesmembuat ini terlihat konyol, tetapi latihan ini tetap penting bagi pelajar.

Anda tidak akan melihat potensi penuh dari mapReducesini, tetapi saya membagikan jawaban ini karena menarik untuk melihat berapa banyak tempat yang dapat diterapkan. Jika Anda tertarik pada bagaimana ini diturunkan dan cara lain itu bisa berguna, silakan lihat jawaban ini .

Terima kasih
sumber
4
Mari kita gunakan Mapdan Map.entries, OK: D. Saya bosan menyalahgunakan objek biasa sebagai tipe data peta.
2
Saya setuju Mapharus digunakan di mana / bila mungkin, tetapi itu tidak sepenuhnya menggantikan kebutuhan untuk menggunakan prosedur ini pada objek JS biasa tho: D
Terima kasih
2

Berdasarkan jawaban @Amberlamps, inilah fungsi utilitas (sebagai komentar itu terlihat jelek)

function mapObject(obj, mapFunc){
    return Object.keys(obj).reduce(function(newObj, value) {
        newObj[value] = mapFunc(obj[value]);
        return newObj;
    }, {});
}

dan penggunaannya adalah:

var obj = {a:1, b:3, c:5}
function double(x){return x * 2}

var newObj = mapObject(obj, double);
//=>  {a: 2, b: 6, c: 10}
yonatanmn
sumber
2
var myObject = { 'a': 1, 'b': 2, 'c': 3 };


Object.prototype.map = function(fn){
    var oReturn = {};
    for (sCurObjectPropertyName in this) {
        oReturn[sCurObjectPropertyName] = fn(this[sCurObjectPropertyName], sCurObjectPropertyName);
    }
    return oReturn;
}
Object.defineProperty(Object.prototype,'map',{enumerable:false});





newObject = myObject.map(function (value, label) {
    return value * value;
});


// newObject is now { 'a': 1, 'b': 4, 'c': 9 }
Dmitry Ragozin
sumber
enumerabledefaultsfalse
Terima kasih
2

Respons saya sebagian besar didasarkan pada respons berperingkat tertinggi di sini dan mudah-mudahan semua orang mengerti (juga memiliki penjelasan yang sama pada GitHub saya). Inilah mengapa penerapannya pada peta bekerja:

Object.keys(images).map((key) => images[key] = 'url(' + '"' + images[key] + '"' +    
')');

Tujuan dari fungsi ini adalah untuk mengambil objek dan memodifikasi konten asli objek menggunakan metode yang tersedia untuk semua objek (objek dan array sama) tanpa mengembalikan array. Hampir semua yang ada di dalam JS adalah objek, dan untuk alasan itu, elemen-elemen yang lebih jauh di dalam pipa pewarisan berpotensi dapat secara teknis menggunakan yang tersedia untuk yang berada di garis atas (dan sebaliknya muncul).

Alasan bahwa ini berfungsi adalah karena fungsi .map mengembalikan array yang MEMBUTUHKAN bahwa Anda memberikan PENGEMBALIAN array secara eksplisit atau implisit alih-alih hanya memodifikasi objek yang ada. Anda pada dasarnya mengelabui program untuk berpikir objek adalah array dengan menggunakan Object.keys yang akan memungkinkan Anda untuk menggunakan fungsi peta dengan aktingnya pada nilai-nilai yang terkait dengan kunci individu (saya sebenarnya secara tidak sengaja mengembalikan array tetapi memperbaikinya). Selama tidak ada pengembalian dalam arti normal, tidak akan ada array yang dibuat dengan objek asli masih utuh dan dimodifikasi seperti yang diprogram.

Program khusus ini mengambil objek yang disebut gambar dan mengambil nilai kunci dan menambahkan tag url untuk digunakan dalam fungsi lain. Asli adalah ini:

var images = { 
snow: 'https://www.trbimg.com/img-5aa059f5/turbine/bs-md-weather-20180305', 
sunny: 'http://www.cubaweather.org/images/weather-photos/large/Sunny-morning-east-   
Matanzas-city- Cuba-20170131-1080.jpg', 
rain: 'https://i.pinimg.com/originals/23/d8
/ab/23d8ab1eebc72a123cebc80ce32b43d8.jpg' };

... dan dimodifikasi adalah ini:

var images = { 
snow: url('https://www.trbimg.com/img-5aa059f5/turbine/bs-md-weather-20180305'),     
sunny: url('http://www.cubaweather.org/images/weather-photos/large/Sunny-morning-   
east-Matanzas-city- Cuba-20170131-1080.jpg'), 
rain: url('https://i.pinimg.com/originals/23/d8
/ab/23d8ab1eebc72a123cebc80ce32b43d8.jpg') 
};

Struktur asli objek dibiarkan utuh memungkinkan untuk akses properti normal selama tidak ada pengembalian. JANGAN memilikinya mengembalikan array seperti biasa dan semuanya akan baik-baik saja. Tujuannya adalah MENGUBAH nilai-nilai asli (gambar [kunci]) untuk apa yang diinginkan dan bukan yang lain. Sejauh yang saya tahu, untuk mencegah output array TELAH ada REASSIGNMENT gambar [kunci] dan tidak ada permintaan implisit atau eksplisit untuk mengembalikan array (tugas variabel melakukan ini dan glitching bolak-balik untuk saya).

EDIT:

Pergi ke alamat metode lain mengenai penciptaan objek baru untuk menghindari memodifikasi objek asli (dan penugasan ulang tampaknya masih diperlukan untuk menghindari sengaja membuat array sebagai output). Fungsi-fungsi ini menggunakan sintaks panah dan jika Anda hanya ingin membuat objek baru untuk digunakan di masa depan.

const mapper = (obj, mapFn) => Object.keys(obj).reduce((result, key) => {
                result[key] = mapFn(obj)[key];
                return result;
            }, {});

var newImages = mapper(images, (value) => value);

Cara kerja fungsi-fungsi ini adalah seperti ini:

mapFn mengambil fungsi untuk ditambahkan nanti (dalam hal ini (nilai) => nilai) dan hanya mengembalikan apa pun yang disimpan di sana sebagai nilai untuk kunci itu (atau dikalikan dua jika Anda mengubah nilai kembali seperti yang dilakukannya) di mapFn ( obj) [key],

dan kemudian mendefinisikan kembali nilai asli yang terkait dengan kunci dalam hasil [key] = mapFn (obj) [key]

dan mengembalikan operasi yang dilakukan pada hasil (akumulator yang terletak di kurung dimulai pada akhir fungsi .reduce).

Semua ini dilakukan pada objek yang dipilih dan MASIH di sana TIDAK BISA menjadi permintaan implisit untuk array yang dikembalikan dan hanya berfungsi ketika menetapkan ulang nilai sejauh yang saya tahu. Ini membutuhkan beberapa senam mental tetapi mengurangi garis kode yang diperlukan seperti yang dapat dilihat di atas. Output persis sama seperti yang bisa dilihat di bawah ini:

{snow: "https://www.trbimg.com/img-5aa059f5/turbine/bs-   
md-weather-20180305", sunny: "http://www.cubaweather.org/images/weather-
photos/lmorning-east-Matanzas-city-Cuba-20170131-1080.jpg", rain: 
"https://i.pinimg.com/originals/23/d8
/ab/23d8ab1eebc72a123cebc80ce32b43d8.jpg"}

Ingat ini bekerja dengan NON-NUMBERS. Anda BISA menduplikasi objek APAPUN dengan SIMPLY MENGEMBALIKAN NILAI di fungsi mapFN.

Andrew Ramshaw
sumber
2

Fungsi pertama menjawab pertanyaan itu. Itu menciptakan peta.

Fungsi kedua tidak membuat peta. Alih-alih, itu loop melalui properti di objek yang ada menggunakan hasOwnProperty(). Alternatif peta ini mungkin merupakan solusi yang lebih baik dalam beberapa situasi.

var myObject = { 'a': 1, 'b': 2, 'c': 3 }


//creates a map (answers question, but function below is 
//much better in some situations)
var newObject = {};
makeMap();    
function makeMap() {
    for (var k in myObject) {
        var value = myObject[k];
        newObject[k] = value * value;
    }
    console.log(newObject); //mapped array
}


//Doesn't create a map, just applies the function to
//a specific property using existing data
function getValue(key) {
  for (var k in myObject) {
    if (myObject.hasOwnProperty(key)) {
      var value = myObject[key]
      return value * value; //stops iteration
    }
  }
}
Input: <input id="input" value="" placeholder="a, b or c"><br>
Output:<input id="output"><br>
<button onclick="output.value=getValue(input.value)" >Get value</button>

Victor Stoddard
sumber
2
Ini tidak menjawab pertanyaan awal - ini tidak memetakan nilai-nilai ke objek baru dengan fungsi yang disediakan (yaitu ini tidak seperti Array.prototype.map).
Matt Browne
@MattBrowne Saya menambahkan metode kedua yang menciptakan apa yang bisa dibilang peta. Ini lambat, tetapi melakukan apa yang diminta OP. Metode pertama ideal dalam banyak situasi, itulah sebabnya saya menggunakan fungsi loop dan properti.
Victor Stoddard
1

Jika Anda tertarik untuk melakukan mapping tidak hanya nilai tetapi juga kunci, saya telah menulis Object.map(valueMapper, keyMapper), yang berlaku seperti ini:

var source = { a: 1, b: 2 };
function sum(x) { return x + x }

source.map(sum);            // returns { a: 2, b: 4 }
source.map(undefined, sum); // returns { aa: 1, bb: 2 }
source.map(sum, sum);       // returns { aa: 2, bb: 4 }
MattiSG
sumber
3
Saya berasumsi karena pada dasarnya Anda hanya menyediakan tautan sebagai jawaban, yang tidak disukai.
Chris Wright
Jika pernah berguna bagi siapa saja: npm install @mattisg/object.map.
MattiSG
1

Saya membutuhkan versi yang memungkinkan memodifikasi kunci juga (berdasarkan jawaban @Amberlamps dan @yonatanmn);

var facts = [ // can be an object or array - see jsfiddle below
    {uuid:"asdfasdf",color:"red"},
    {uuid:"sdfgsdfg",color:"green"},
    {uuid:"dfghdfgh",color:"blue"}
];

var factObject = mapObject({}, facts, function(key, item) {
    return [item.uuid, {test:item.color, oldKey:key}];
});

function mapObject(empty, obj, mapFunc){
    return Object.keys(obj).reduce(function(newObj, key) {
        var kvPair = mapFunc(key, obj[key]);
        newObj[kvPair[0]] = kvPair[1];
        return newObj;
    }, empty);
}

factObject =

{
"asdfasdf": {"color":"red","oldKey":"0"},
"sdfgsdfg": {"color":"green","oldKey":"1"},
"dfghdfgh": {"color":"blue","oldKey":"2"}
}

Sunting: sedikit perubahan untuk melewati objek awal {}. Mengizinkannya menjadi [] (jika kunci bilangan bulat)

Simetris
sumber
1

Saya secara khusus ingin menggunakan fungsi yang sama yang saya gunakan untuk array untuk objek tunggal, dan ingin membuatnya tetap sederhana. Ini bekerja untuk saya:

var mapped = [item].map(myMapFunction).pop();
Jason Norwood-Young
sumber
ini tidak berbeda denganvar mapped = myMapFunction(item)
Terima kasih
Ini sangat sederhana daripada semua solusi "peningkatan roket" lainnya yang saya lihat di sini. Terima kasih telah berbagi :)
Yuri Teixeira
1

Untuk merespons lebih dekat dengan apa tepatnya yang diminta OP, OP menginginkan objek:

myObject = { 'a': 1, 'b': 2, 'c': 3 }

untuk memiliki metode peta myObject.map,

mirip dengan Array.prototype.map yang akan digunakan sebagai berikut:

newObject = myObject.map (fungsi (nilai, label) {
    nilai pengembalian * nilai;
});
// newObject sekarang {'a': 1, 'b': 4, 'c': 9}

Jawaban imho terbaik (diukur dengan istilah " mendekati apa yang diminta " + "tidak ada ES {5,6,7} diperlukan tanpa perlu") adalah:

myObject.map = function mapForObject(callback)
{
  var result = {};
  for(var property in this){
    if(this.hasOwnProperty(property) && property != "map"){
      result[property] = callback(this[property],property,this);
    }
  }
  return result;
}

Kode di atas menghindari sengaja menggunakan fitur bahasa apa pun, hanya tersedia dalam edisi ECMAScript terbaru. Dengan kode di atas masalah bisa diselesaikan seperti ini:

myObject = { 'a': 1, 'b': 2, 'c': 3 };

myObject.map = function mapForObject(callback)
{
  var result = {};
  for(var property in this){
    if(this.hasOwnProperty(property) && property != "map"){
      result[property] = callback(this[property],property,this);
    }
  }
  return result;
}

newObject = myObject.map(function (value, label) {
  return value * value;
});
console.log("newObject is now",newObject);
kode uji alternatif di sini

Selain disukai oleh beberapa orang, itu akan menjadi kemungkinan untuk memasukkan solusi dalam rantai prototipe seperti ini.

Object.prototype.map = function(callback)
{
  var result = {};
  for(var property in this){
    if(this.hasOwnProperty(property)){
      result[property] = callback(this[property],property,this);
    }
  }
  return result;
}

Sesuatu, yang ketika dilakukan dengan pengawasan yang cermat seharusnya tidak memiliki efek buruk dan tidak mempengaruhi mapmetode objek lain (yaitu Array's map).

humanityANDpeace
sumber
1

Tentukan fungsi mapEntries.

mapEntries mengambil fungsi callback yang dipanggil pada setiap entri di objek dengan nilai parameter, kunci dan objek. Itu harus mengembalikan nilai baru.

mapEntries harus mengembalikan objek baru dengan nilai baru yang dikembalikan dari callback.

Object.defineProperty(Object.prototype, 'mapEntries', {
  enumerable: false,
  value: function (mapEntriesCallback) {
    return Object.fromEntries(
      Object.entries(this).map(
        ([key, value]) => [key, mapEntriesCallback(value, key, this)]
      )
    )
  }
})


// Usage example:

var object = {a: 1, b: 2, c: 3}
var newObject = object.mapEntries(value => value * value)
console.log(newObject)
//> {a: 1, b: 4, c: 9}

Sunting: Versi sebelumnya tidak menentukan bahwa ini bukan properti enumerable

Arye Eidelman
sumber
0

Hei menulis fungsi mapper kecil yang mungkin bisa membantu.

    function propertyMapper(object, src){
         for (var property in object) {   
           for (var sourceProp in src) {
               if(property === sourceProp){
                 if(Object.prototype.toString.call( property ) === '[object Array]'){
                   propertyMapper(object[property], src[sourceProp]);
                   }else{
                   object[property] = src[sourceProp];
                }
              }
            }
         }
      }
Zak
sumber
1
Kurasa maksudmu "Aku", bukan "Hei"
Carlo
0

Pandangan yang berbeda untuk itu adalah menggunakan fungsi string json kustom yang juga dapat bekerja pada objek yang dalam. Ini mungkin berguna jika Anda bermaksud mempostingnya ke server sebagai json

const obj = { 'a': 1, 'b': 2, x: {'c': 3 }}
const json = JSON.stringify(obj, (k, v) => typeof v === 'number' ? v * v : v)

console.log(json)
console.log('back to json:', JSON.parse(json))

Tak berujung
sumber
0

Saya hanya menangani string untuk mengurangi pengecualian:

Object.keys(params).map(k => typeof params[k] == "string" ? params[k] = params[k].trim() : null);
Idan
sumber
0

Object Mapper dalam TypeScript

Saya suka contoh yang digunakan Object.fromEntriesseperti ini , tapi tetap saja, mereka tidak mudah digunakan. Jawaban yang menggunakan Object.keysdan kemudian mencari keysebenarnya melakukan beberapa pencarian yang mungkin tidak diperlukan.

Saya berharap ada Object.mapfungsi, tetapi kita dapat membuatnya sendiri dan menyebutnya objectMapdengan kemampuan untuk memodifikasi keduanya keydan value:

Penggunaan (JavaScript):

const myObject = { 'a': 1, 'b': 2, 'c': 3 };

// keep the key and modify the value
let obj = objectMap(myObject, val => val * 2);
// obj = { a: 2, b: 4, c: 6 }


// modify both key and value
obj = objectMap(myObject,
    val => val * 2 + '',
    key => (key + key).toUpperCase());
// obj = { AA: '2', BB: '4', CC: '6' }

Kode (TypeScript):

interface Dictionary<T> {
    [key: string]: T;
}

function objectMap<TValue, TResult>(
    obj: Dictionary<TValue>,
    valSelector: (val: TValue, obj: Dictionary<TValue>) => TResult,
    keySelector?: (key: string, obj: Dictionary<TValue>) => string,
    ctx?: Dictionary<TValue>
) {
    const ret = {} as Dictionary<TResult>;
    for (const key of Object.keys(obj)) {
        const retKey = keySelector
            ? keySelector.call(ctx || null, key, obj)
            : key;
        const retVal = valSelector.call(ctx || null, obj[key], obj);
        ret[retKey] = retVal;
    }
    return ret;
}

Jika Anda tidak menggunakan TypeScript maka salin kode di atas di TypeScript Playground untuk mendapatkan kode JavaScript.

Juga, alasan saya memasukkan keySelectorsetelah valSelectordalam daftar parameter, adalah karena itu opsional.

* Beberapa pujian untuk jawaban pabrik alexander .

orad
sumber
0
const mapObject = (targetObject, callbackFn) => {
    if (!targetObject) return targetObject;
    if (Array.isArray(targetObject)){
        return targetObject.map((v)=>mapObject(v, callbackFn))
    }
    return Object.entries(targetObject).reduce((acc,[key, value]) => {
        const res = callbackFn(key, value);
        if (!Array.isArray(res) && typeof res ==='object'){
            return {...acc, [key]: mapObject(res, callbackFn)}
        }
        if (Array.isArray(res)){
            return {...acc, [key]: res.map((v)=>mapObject(v, callbackFn))}
        }
        return {...acc, [key]: res};
    },{})
};
const mapped = mapObject(a,(key,value)=> {
    if (!Array.isArray(value) && key === 'a') return ;
    if (!Array.isArray(value) && key === 'e') return [];
    if (!Array.isArray(value) && key === 'g') return value * value;
    return value;
});
console.log(JSON.stringify(mapped)); 
// {"b":2,"c":[{"d":2,"e":[],"f":[{"g":4}]}]}

Fungsi ini berjalan secara rekursif melalui objek dan array objek. Atribut dapat dihapus jika dikembalikan tidak terdefinisi

Stanislav
sumber