apakah ada fungsi dalam lodash untuk mengganti item yang cocok

134

Saya ingin tahu apakah ada metode yang lebih sederhana dalam lodash untuk mengganti item dalam koleksi JavaScript? (Kemungkinan duplikat tapi saya tidak mengerti jawabannya di sana :)

Saya melihat dokumentasi mereka tetapi tidak dapat menemukan apa pun

Kode saya adalah:

var arr = [{id: 1, name: "Person 1"}, {id:2, name:"Person 2"}];
// Can following code be reduced to something like _.XX(arr, {id:1}, {id:1, name: "New Name"});
_.each(arr, function(a, idx){
  if(a.id === 1){
    arr[idx] = {id:1, name: "Person New Name"};
    return false;
  }
});

_.each(arr, function(a){
  document.write(a.name);
});

Pembaruan: Objek yang saya coba ganti memiliki banyak properti seperti

{id: 1, Prop1: ..., Prop2: ..., dan seterusnya}

Larutan:

Terima kasih kepada dfsq tapi saya menemukan solusi yang tepat di dalam lodash yang tampaknya berfungsi dengan baik dan cukup rapi dan saya memasukkannya ke dalam mixin juga karena saya memiliki persyaratan ini di banyak tempat. JSBin

var update = function(arr, key, newval) {
  var match = _.find(arr, key);
  if(match)
    _.merge(match, newval);
  else
    arr.push(newval);    
};

_.mixin({ '$update': update });

var arr = [{id: 1, name: "Person 1"}, {id:2, name:"Person 2"}];

_.$update(arr, {id:1}, {id:1, name: "New Val"});


document.write(JSON.stringify(arr));

Solusi Lebih Cepat Seperti yang ditunjukkan oleh @dfsq, mengikuti adalah cara yang lebih cepat

var upsert = function (arr, key, newval) {
    var match = _.find(arr, key);
    if(match){
        var index = _.indexOf(arr, _.find(arr, key));
        arr.splice(index, 1, newval);
    } else {
        arr.push(newval);
    }
};
Vishal Seth
sumber
7
Saya pikir Anda dapat menggunakan pertandingan sebagai param kedua ke _.indexOf pada liine 4 dari "Solusi Lebih Cepat" Anda juga, tidak perlu menghitung ulang nilai itu di sana, yang seharusnya membuat segalanya sedikit lebih cepat.
davertron
2
Bahkan lebih cepat: gunakan _.findIndexuntuk pertandingan.
Julian K
1
Hanya untuk memperluas apa yang dikatakan @JulianK dan @davertron, menggunakan _.findIndexalih-alih _.findakan membiarkan Anda menghapus yang kedua _.finddan yang kedua _.indexOf. Anda mengulangi array sebanyak 3 kali ketika yang Anda butuhkan adalah 1.
Justin Morgan

Jawaban:

191

Dalam kasus Anda, yang perlu Anda lakukan adalah menemukan objek dalam array dan menggunakan Array.prototype.splice()metode, baca detail selengkapnya di sini :

var arr = [{id: 1, name: "Person 1"}, {id:2, name:"Person 2"}];

// Find item index using _.findIndex (thanks @AJ Richardson for comment)
var index = _.findIndex(arr, {id: 1});

// Replace item at index using native splice
arr.splice(index, 1, {id: 100, name: 'New object.'});

// "console.log" result
document.write(JSON.stringify( arr ));
<script src="//cdnjs.cloudflare.com/ajax/libs/lodash.js/2.4.1/lodash.min.js"></script>

dfsq
sumber
1
Nah solusi Anda akan lebih mahal dalam hal kinerja, karena indexOfakan sangat cepat (itu akan menggunakan browser asli Array.prototype.indexOf). Tapi bagaimanapun, senang Anda menemukan solusi yang cocok untuk Anda.
dfsq
14
Kenapa tidak digunakan _.findIndex? Maka Anda tidak perlu menggunakan _.indexOf.
AJ Richardson
35

Sepertinya solusi paling sederhana untuk menggunakan ES6 .mapatau lodash _.map:

var arr = [{id: 1, name: "Person 1"}, {id: 2, name: "Person 2"}];

// lodash
var newArr = _.map(arr, function(a) {
  return a.id === 1 ? {id: 1, name: "Person New Name"} : a;
});

// ES6
var newArr = arr.map(function(a) {
  return a.id === 1 ? {id: 1, name: "Person New Name"} : a;
});

Ini memiliki efek yang bagus untuk menghindari mutasi array asli.

Spencer
sumber
8
Tapi Anda membuat array baru setiap kali ... Layak untuk dicatat.
kboom
3
Satu-satunya alternatif untuk tidak membuat array baru adalah mengubah yang sudah ada. Juga, membuat array baru kemungkinan tidak akan berdampak dalam hal kinerja. Suara positif dari saya.
Nobita
24

[ES6] Kode ini berfungsi untuk saya.

let result = array.map(item => item.id === updatedItem.id ? updatedItem : item)
shebik
sumber
1. Anda membuat instance array baru, jadi tidak benar "mengganti" item. 2. Anda akan kehilangan updatedItemarray Anda jika tidak termasuk item dengan yang sama id.
jahat
ini adalah solusi untuk 'perbarui' bukan 'upsert' (pertanyaannya adalah "apakah ada fungsi di lodash untuk mengganti item MATCHED") dan ya, itu menciptakan salinan array, jadi jangan gunakan itu jika Anda perlu bekerja dengan array yang sama (saya tidak)
shebik
21
function findAndReplace(arr, find, replace) {
  let i;
  for(i=0; i < arr.length && arr[i].id != find.id; i++) {}
  i < arr.length ? arr[i] = replace : arr.push(replace);
}

Sekarang mari kita uji kinerja untuk semua metode:

jahat
sumber
6
Diremehkan karena bersikap negatif terhadap orang-orang ("apakah ini semacam lelucon") membuat mereka tidak belajar. Bayangkan jika saya menyelesaikan ini dengan "menjadi orang yang cerdas saya berharap Anda tidak malas tentang emosi Anda dan berpikir tentang itu".
Aditya MP
5
Saya tidak ingin menyakiti siapa pun, tetapi saya bertanya-tanya bagaimana solusi yang paling buruk dari pendekatan pembuat topik asli mendapat begitu banyak suara. Apa yang mengatur orang-orang yang memberikan suara mereka untuk itu? Dan saya kecewa bahwa orang secara membuta percaya pada jawaban yang paling banyak dipilih dan tidak memiliki pemikiran kritis.
jahat
1
@ Evilive Valid poin, tapi saya tidak melihat bagaimana mereka mengharuskan Anda untuk menemukan seolah-olah semua orang yang sebelumnya memberikan jawaban / suara adalah idiot. Bagian faktual dari jawaban ini sangat bagus, sisanya memiliki suasana superioritas yang nyaris tidak berisi. Itu tidak membantu siapa pun. Anda dapat dengan mudah menyampaikan poin Anda tanpa respons emosional yang berlebihan.
Thor84no
1
Perlu dicatat bahwa solusi Anda dan solusi TC hanya memfilter menurut id. Itulah alasan pertama bahwa keduanya berjalan lebih cepat. Dua lainnya memungkinkan Anda untuk melewati bagian apa pun dari objek yang Anda butuhkan untuk pemfilteran yang mungkin lebih disukai sebagai fungsi yang lebih baik.
Aram
10

Anda juga dapat menggunakan findIndex dan memilih untuk mencapai hasil yang sama:

  var arr  = [{id: 1, name: "Person 1"}, {id:2, name:"Person 2"}];
  var data = {id: 2, name: 'Person 2 (updated)'};
  var index = _.findIndex(arr, _.pick(data, 'id'));
  if( index !== -1) {
    arr.splice(index, 1, data);
  } else {
    arr.push(data);
  }
JVitela
sumber
6

Seiring berjalannya waktu Anda harus merangkul pendekatan yang lebih fungsional di mana Anda harus menghindari mutasi data dan menulis kecil, fungsi tanggung jawab tunggal. Dengan standar ECMAScript 6, Anda dapat menikmati paradigma pemrograman fungsional dalam JavaScript dengan yang disediakan map, filterdan reducemetode. Anda tidak perlu tempat lain, garis bawah atau apa lagi untuk melakukan hal-hal paling mendasar.

Di bawah ini saya telah menyertakan beberapa solusi yang diusulkan untuk masalah ini untuk menunjukkan bagaimana masalah ini dapat diselesaikan dengan menggunakan fitur bahasa yang berbeda:

Menggunakan peta ES6:

const replace = predicate => replacement => element =>
  predicate(element) ? replacement : element
 
const arr = [ { id: 1, name: "Person 1" }, { id:2, name:"Person 2" } ];
const predicate = element => element.id === 1
const replacement = { id: 100, name: 'New object.' }

const result = arr.map(replace (predicate) (replacement))
console.log(result)


Versi rekursif - setara dengan pemetaan:

Membutuhkan perusakan dan penyebaran array .

const replace = predicate => replacement =>
{
  const traverse = ([head, ...tail]) =>
    head
    ? [predicate(head) ? replacement : head, ...tail]
    : []
  return traverse
}
 
const arr = [ { id: 1, name: "Person 1" }, { id:2, name:"Person 2" } ];
const predicate = element => element.id === 1
const replacement = { id: 100, name: 'New object.' }

const result = replace (predicate) (replacement) (arr)
console.log(result)


Ketika urutan larik akhir tidak penting, Anda dapat menggunakan struktur data objectsebagai HashMap . Sangat berguna jika Anda sudah memiliki koleksi kunci sebagai object- jika tidak, Anda harus mengubah representasi Anda terlebih dahulu.

Membutuhkan penyebaran sisa objek , nama properti yang dihitung, dan Object.entries .

const replace = key => ({id, ...values}) => hashMap =>
({
  ...hashMap,       //original HashMap
  [key]: undefined, //delete the replaced value
  [id]: values      //assign replacement
})

// HashMap <-> array conversion
const toHashMapById = array =>
  array.reduce(
    (acc, { id, ...values }) => 
    ({ ...acc, [id]: values })
  , {})
  
const toArrayById = hashMap =>
  Object.entries(hashMap)
  .filter( // filter out undefined values
    ([_, value]) => value 
  ) 
  .map(
    ([id, values]) => ({ id, ...values })
  )

const arr = [ { id: 1, name: "Person 1" }, { id:2, name:"Person 2" } ];
const replaceKey = 1
const replacement = { id: 100, name: 'New object.' }

// Create a HashMap from the array, treating id properties as keys
const hashMap = toHashMapById(arr)
console.log(hashMap)

// Result of replacement - notice an undefined value for replaced key
const resultHashMap = replace (replaceKey) (replacement) (hashMap)
console.log(resultHashMap)

// Final result of conversion from the HashMap to an array
const result = toArrayById (resultHashMap)
console.log(result)

Przemysław Zalewski
sumber
5

Jika Anda hanya mencoba mengganti satu properti, lodash _.finddan _.setseharusnya sudah cukup:

var arr = [{id: 1, name: "Person 1"}, {id: 2, name: "Person 2"}];

_.set(_.find(arr, {id: 1}), 'name', 'New Person');
Andrei Gavrilov
sumber
1

Jika titik penyisipan objek baru tidak perlu cocok dengan indeks objek sebelumnya maka cara paling sederhana untuk melakukan ini dengan lodash adalah dengan menggunakan _.rejectdan kemudian mendorong nilai-nilai baru ke dalam array:

var arr = [
  { id: 1, name: "Person 1" }, 
  { id: 2, name: "Person 2" }
];

arr = _.reject(arr, { id: 1 });
arr.push({ id: 1, name: "New Val" });

// result will be: [{ id: 2, name: "Person 2" }, { id: 1, name: "New Val" }]

Jika Anda memiliki beberapa nilai yang ingin Anda ganti dalam satu pass, Anda dapat melakukan hal berikut (ditulis dalam format non-ES6):

var arr = [
  { id: 1, name: "Person 1" }, 
  { id: 2, name: "Person 2" }, 
  { id: 3, name: "Person 3" }
];

idsToReplace = [2, 3];
arr = _.reject(arr, function(o) { return idsToReplace.indexOf(o.id) > -1; });
arr.push({ id: 3, name: "New Person 3" });
arr.push({ id: 2, name: "New Person 2" });


// result will be: [{ id: 1, name: "Person 1" }, { id: 3, name: "New Person 3" }, { id: 2, name: "New Person 2" }]
kaya
sumber
metode ini mengubah penyortiran array
sospedra
1

Dengan menggunakan lodash unionDengan fungsi, Anda dapat melakukan gerakan sederhana ke sebuah objek. Dokumentasi menyatakan bahwa jika ada kecocokan, itu akan menggunakan array pertama. Bungkus objek Anda yang diperbarui dalam [] (array) dan letakkan sebagai array pertama dari fungsi gabungan. Cukup tentukan logika pencocokan Anda dan jika ditemukan akan menggantinya dan jika tidak maka akan menambahkannya

Contoh:

let contacts = [
     {type: 'email', desc: 'work', primary: true, value: 'email prim'}, 
     {type: 'phone', desc: 'cell', primary: true, value:'phone prim'},
     {type: 'phone', desc: 'cell', primary: false,value:'phone secondary'},
     {type: 'email', desc: 'cell', primary: false,value:'email secondary'}
]

// Update contacts because found a match
_.unionWith([{type: 'email', desc: 'work', primary: true, value: 'email updated'}], contacts, (l, r) => l.type == r.type && l.primary == r.primary)

// Add to contacts - no match found
_.unionWith([{type: 'fax', desc: 'work', primary: true, value: 'fax added'}], contacts, (l, r) => l.type == r.type && l.primary == r.primary)
Jeffrey
sumber
1

Varian tidak terlalu buruk)

var arr = [{id: 1, name: "Person 1"}, {id: 2, name: "Person 2"}];

var id = 1; //id to find

arr[_.find(arr, {id: id})].name = 'New Person';
Ivan Pirus
sumber
1
var arr= [{id: 1, name: "Person 1"}, {id:2, name:"Person 2"}];
var index = _.findIndex(arr, {id: 1});
arr[index] = {id: 100, name: 'xyz'}
Amit Chhatbar
sumber
0

Jika Anda sedang mencari cara untuk mengubah koleksi secara permanen (seperti saya ketika saya menemukan pertanyaan Anda), Anda mungkin dapat melihat pembantu-ketidakberdayaan , sebuah perpustakaan yang bercabang dari utilitas Bereaksi asli. Dalam kasus Anda, Anda akan mencapai apa yang Anda sebutkan melalui yang berikut:

var update = require('immutability-helper')
var arr = [{id: 1, name: "Person 1"}, {id:2, name:"Person 2"}]
var newArray = update(arr, { 0: { name: { $set: 'New Name' } } })
//=> [{id: 1, name: "New Name"}, {id:2, name:"Person 2"}]
Aaron_H
sumber
0

Anda bisa melakukannya tanpa menggunakan lodash.

let arr = [{id: 1, name: "Person 1"}, {id: 2, name: "Person 2"}];
let newObj = {id: 1, name: "new Person"}

/*Add new prototype function on Array class*/
Array.prototype._replaceObj = function(newObj, key) {
  return this.map(obj => (obj[key] === newObj[key] ? newObj : obj));
};

/*return [{id: 1, name: "new Person"}, {id: 2, name: "Person 2"}]*/
arr._replaceObj(newObj, "id") 
Cerah
sumber
0

Abadi , cocok untuk ReactJS:

Menganggap:

cosnt arr = [{id: 1, name: "Person 1"}, {id:2, name:"Person 2"}];

Item yang diperbarui adalah yang kedua dan nama diubah menjadi Special Person:

const updatedItem = {id:2, name:"Special Person"};

Petunjuk : yang lodash memiliki alat yang berguna tapi sekarang kami memiliki beberapa dari mereka di Ecmascript6 +, jadi saya hanya menggunakanmapfungsi yang ada pada kedualodashdanecmascript6+:

const newArr = arr.map(item => item.id === 2 ? updatedItem : item);
Amerika
sumber
0

Datang juga dan lakukan dengan cara itu.

const persons = [{id: 1, name: "Person 1"}, {id:2, name:"Person 2"}];
const updatedPerson = {id: 1, name: "new Person Name"}
const updatedPersons = persons.map(person => (
  person.id === updated.id
    ? updatedPerson
    : person
))

Kalau mau kita bisa menggeneralisasikannya

const replaceWhere = (list, predicate, replacement) => {
  return list.map(item => predicate(item) ? replacement : item)
}

replaceWhere(persons, person => person.id === updatedPerson.id, updatedPerson)
rmmjohann
sumber