Gibt es eine Funktion in lodash, um übereinstimmende Artikel zu ersetzen?

133

Ich frage mich, ob es in lodash eine einfachere Methode gibt, um ein Element in einer JavaScript-Sammlung zu ersetzen. (Mögliches Duplikat, aber ich habe die Antwort dort nicht verstanden :)

Ich sah mir ihre Unterlagen an, konnte aber nichts finden

Mein Code lautet:

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

Update: Das Objekt, durch das ich ersetzen möchte, hat viele Eigenschaften wie

{id: 1, Prop1: ..., Prop2: ... und so weiter}

Lösung:

Dank dfsq habe ich aber in lodash eine richtige Lösung gefunden, die gut zu funktionieren scheint und ziemlich ordentlich ist, und ich habe sie auch in ein Mixin eingefügt, da ich diese Anforderung an vielen Stellen habe. 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));

Schnellere Lösung Wie von @dfsq hervorgehoben, ist das Folgen viel schneller

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
quelle
7
Ich denke, Sie können match auch als zweiten Parameter für _.indexOf in Zeile 4 Ihrer "Faster Solution" verwenden, ohne dass Sie diesen Wert dort neu berechnen müssen, was die Dinge etwas schneller machen sollte.
Davertron
2
Noch schneller: _.findIndexfür Match verwenden.
Julian K
1
Nur um zu erweitern, was @JulianK und @davertron gesagt haben, können Sie mit _.findIndexanstelle von _.findsowohl die zweite _.findals auch die zweite löschen _.indexOf. Sie durchlaufen das Array dreimal, wenn Sie nur 1 benötigen.
Justin Morgan

Antworten:

191

In Ihrem Fall müssen Sie lediglich das Objekt im Array finden und die Array.prototype.splice()Methode verwenden. Weitere Informationen finden Sie hier :

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
quelle
1
Nun, Ihre Lösung wird mehr an Leistung kosten, da indexOfsie sehr schnell sein wird (es werden native Browser Array.prototype.indexOf verwendet). Trotzdem bin ich froh, dass Sie eine Lösung finden, die für Sie funktioniert.
dfsq
13
Warum nicht verwenden _.findIndex? Dann brauchen Sie nicht zu verwenden _.indexOf.
AJ Richardson
35

Die einfachste Lösung scheint die Verwendung von ES6 .mapoder lodash zu sein _.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;
});

Dies hat den schönen Effekt, dass das ursprüngliche Array nicht mutiert wird.

Spencer
quelle
8
Aber Sie erstellen jedes Mal ein neues Array ... Bemerkenswert.
Kboom
3
Die einzige Alternative, kein neues Array zu erstellen, besteht darin, das vorhandene zu mutieren. Das Erstellen eines neuen Arrays hat wahrscheinlich keine Auswirkungen auf die Leistung. Upvote von mir.
Nobita
24

[ES6] Dieser Code funktioniert für mich.

let result = array.map(item => item.id === updatedItem.id ? updatedItem : item)
shebik
quelle
1. Sie erstellen eine neue Instanz eines Arrays, daher ist es nicht wahr, ein Element zu "ersetzen". 2. Sie verlieren Ihr, updatedItemwenn das Array kein Element mit demselben enthält id.
böse
Dies ist eine Lösung für 'Update' und nicht für 'Upsert' (die Frage lautete "Gibt es in lodash eine Funktion zum Ersetzen des MATCHED-Elements"). Ja, es wird eine Kopie des Arrays erstellt. Verwenden Sie sie daher nicht, wenn Sie damit arbeiten müssen gleiche Anordnung (ich habe nicht)
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);
}

Testen wir nun die Leistung für alle Methoden:

böse
quelle
6
Abgestimmt, weil das Negative gegenüber Menschen ("ist das eine Art Witz") sie vom Lernen abhält. Stellen Sie sich vor, wenn ich dies mit "ein kluger Mensch zu sein, würde ich erwarten, dass Sie nicht faul in Bezug auf Ihre Gefühle sind und darüber nachdenken".
Aditya MP
5
Ich wollte niemanden verletzen, aber ich frage mich, wie die Lösung, die schlimmer war als der Ansatz des ursprünglichen Themenerstellers, so viele Stimmen erhielt. Welche Regeln haben die Leute, die dafür gestimmt haben? Und ich habe enttäuscht, dass die Leute blind der am meisten gewählten Antwort vertrauen und nicht kritisch denken.
böse
1
@evilive Gültige Punkte, aber ich sehe nicht, wie diese erfordern, dass Sie rüberkommen, als wären alle, die zuvor Antworten / Stimmen gegeben haben, Idioten. Die sachlichen Teile dieser Antwort sind großartig, der Rest wirkt wie ein kaum enthaltener Überlegenheitskomplex. Das hilft niemandem. Sie können Ihre Punkte leicht ohne die übermäßige emotionale Reaktion machen.
Thor84no
1
Es ist jedoch zu beachten, dass Ihre Lösung und die Lösung von TC nur nach IDs filtern. Das ist der erste Grund, warum diese beiden schneller laufen. Mit den anderen beiden können Sie jeden Teil des Objekts übergeben, den Sie zum Filtern benötigen, was als Upsert-Funktion möglicherweise besser geeignet ist.
Aram
10

Sie können auch findIndex und pick verwenden, um das gleiche Ergebnis zu erzielen:

  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
quelle
6

Im Laufe der Zeit sollten Sie einen funktionaleren Ansatz wählen, bei dem Sie Datenmutationen vermeiden und kleine Funktionen mit einer einzigen Verantwortung schreiben sollten. Mit dem ECMAScript 6 - Standard können Sie funktionales Programmierparadigma in JavaScript mit den nutzen map, filterund reduceMethoden. Sie brauchen keinen weiteren Lodash, Unterstrich oder was Sie sonst noch tun müssen, um die grundlegendsten Dinge zu tun.

Unten habe ich einige Lösungsvorschläge für dieses Problem aufgeführt, um zu zeigen, wie dieses Problem mit verschiedenen Sprachfunktionen gelöst werden kann:

Verwenden der ES6-Karte:

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)


Rekursive Version - entspricht der Zuordnung:

Erfordert Destrukturierung und Array-Verbreitung .

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)


Wenn die Reihenfolge des endgültigen Arrays nicht wichtig ist, können Sie eine objectals HashMap- Datenstruktur verwenden. Sehr praktisch, wenn Sie bereits eine Sammlung als Schlüssel eingegeben haben object- andernfalls müssen Sie zuerst Ihre Darstellung ändern.

Erfordert die Verteilung von Objektresten , berechnete Eigenschaftsnamen und 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
quelle
5

Wenn Sie nur versuchen, eine Eigenschaft zu ersetzen, lodash _.findund _.setsollte genug sein:

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

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

Wenn die Einfügemarke des neuen Objekts nicht mit dem Index des vorherigen Objekts übereinstimmen muss, können Sie dies am einfachsten mit lodash tun, _.rejectindem Sie neue Werte verwenden und dann in das Array verschieben:

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" }]

Wenn Sie mehrere Werte haben, die Sie in einem Durchgang ersetzen möchten, können Sie Folgendes tun (im Nicht-ES6-Format geschrieben):

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" }]
richt
quelle
Diese Methode ändert die Array-Sortierung
Sospedra
1

Mit der Funktion lodash unionWith können Sie ein einfaches Upsert für ein Objekt durchführen. In der Dokumentation wird angegeben, dass bei einer Übereinstimmung das erste Array verwendet wird. Wickeln Sie Ihr aktualisiertes Objekt in [] (Array) und fügen Sie es als erstes Array der Vereinigungsfunktion ein. Geben Sie einfach Ihre übereinstimmende Logik an. Wenn sie gefunden wird, wird sie ersetzt und wenn nicht, wird sie hinzugefügt

Beispiel:

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
quelle
1

Keine schlechte Variante auch)

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
quelle
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
quelle
0

Wenn Sie nach einer Möglichkeit suchen, die Sammlung unveränderlich zu ändern (wie ich es war, als ich Ihre Frage gefunden habe), können Sie sich den Unveränderlichkeitshelfer ansehen , eine Bibliothek, die aus dem ursprünglichen React-Util stammt. In Ihrem Fall würden Sie das, was Sie erwähnt haben, über Folgendes erreichen:

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
quelle
0

Sie können es ohne lodash tun.

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") 
Sonnig
quelle
0

Unveränderlich , geeignet für ReactJS:

Annehmen:

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

Das aktualisierte Element ist das zweite und der Name wird geändert in Special Person:

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

Hinweis : Das Lodash hat nützliche Tools, aber jetzt haben wir einige davon in Ecmascript6 +, daher verwende ich nurmapFunktionen, die sowohl fürlodashals auch fürecmascript6+:

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

Kam auch darüber hinweg und tat es einfach so.

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

Wenn gewünscht, können wir es verallgemeinern

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

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