Zastanawiam się, czy w lodash istnieje prostsza metoda zastąpienia elementu w kolekcji JavaScript? (Możliwy duplikat, ale nie rozumiem tam odpowiedzi :)
Przejrzałem ich dokumentację, ale nic nie znalazłem
Mój kod to:
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);
});
Aktualizacja: obiekt, który próbuję zastąpić, ma wiele właściwości, takich jak
{id: 1, Prop1: ..., Prop2: ... i tak dalej}
Rozwiązanie:
Dzięki dfsq, ale znalazłem odpowiednie rozwiązanie w lodash, które wydaje się działać dobrze i jest całkiem schludne i również umieściłem je w miksie, ponieważ mam ten wymóg w wielu miejscach. 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));
Szybsze rozwiązanie Jak wskazał @dfsq, śledzenie jest znacznie szybsze
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);
}
};
javascript
lodash
Vishal Seth
źródło
źródło
_.findIndex
do dopasowania._.findIndex
zamiast_.find
pozwoli ci upuścić zarówno drugi, jak_.find
i_.indexOf
. Przechodzisz przez tablicę 3 razy, kiedy wszystko, czego potrzebujesz, to 1.Odpowiedzi:
W twoim przypadku wszystko co musisz zrobić to znaleźć obiekt w tablicy i użyć
Array.prototype.splice()
metody, przeczytaj więcej szczegółów tutaj :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>
źródło
indexOf
będzie bardzo szybkie (będzie używać natywnych przeglądarek Array.prototype.indexOf). Mimo wszystko cieszę się, że znalazłeś rozwiązanie, które będzie dla Ciebie odpowiednie._.findIndex
? Wtedy nie musisz używać_.indexOf
.Wydaje się, że najprostszym rozwiązaniem byłoby użycie ES6
.map
lub 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; });
Ma to fajny efekt w postaci uniknięcia mutacji oryginalnej tablicy.
źródło
[ES6] Ten kod działa dla mnie.
let result = array.map(item => item.id === updatedItem.id ? updatedItem : item)
źródło
updatedItem
jeśli tablica nie zawiera elementu z tym samymid
.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); }
Teraz przetestujmy wydajność dla wszystkich metod:
Pokaż fragment kodu
// TC's first approach function first(arr, a, b) { _.each(arr, function (x, idx) { if (x.id === a.id) { arr[idx] = b; return false; } }); } // solution with merge function second(arr, a, b) { const match = _.find(arr, a); if (match) { _.merge(match, b); } else { arr.push(b); } } // most voted solution function third(arr, a, b) { const match = _.find(arr, a); if (match) { var index = _.indexOf(arr, _.find(arr, a)); arr.splice(index, 1, b); } else { arr.push(b); } } // my approach function fourth(arr, a, b){ let l; for(l=0; l < arr.length && arr[l].id != a.id; l++) {} l < arr.length ? arr[l] = b : arr.push(b); } function test(fn, times, el) { const arr = [], size = 250; for (let i = 0; i < size; i++) { arr[i] = {id: i, name: `name_${i}`, test: "test"}; } let start = Date.now(); _.times(times, () => { const id = Math.round(Math.random() * size); const a = {id}; const b = {id, name: `${id}_name`}; fn(arr, a, b); }); el.innerHTML = Date.now() - start; } test(first, 1e5, document.getElementById("first")); test(second, 1e5, document.getElementById("second")); test(third, 1e5, document.getElementById("third")); test(fourth, 1e5, document.getElementById("fourth"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.14.1/lodash.min.js"></script> <div> <ol> <li><b id="first"></b> ms [TC's first approach]</li> <li><b id="second"></b> ms [solution with merge]</li> <li><b id="third"></b> ms [most voted solution]</li> <li><b id="fourth"></b> ms [my approach]</li> </ol> <div>
źródło
Możesz również użyć funkcji findIndex i pick, aby osiągnąć ten sam wynik:
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); }
źródło
W miarę upływu czasu powinieneś przyjąć bardziej funkcjonalne podejście, w którym powinieneś unikać mutacji danych i pisać małe funkcje o pojedynczej odpowiedzialności. Ze standardem ECMAScript 6, można korzystać funkcjonalny paradygmat programowania w JavaScript z dostarczonych
map
,filter
ireduce
metod. Nie potrzebujesz kolejnego lodash, podkreślenia ani tego, co jeszcze zrobić, aby zrobić najbardziej podstawowe rzeczy.Poniżej zamieściłem kilka propozycji rozwiązań tego problemu, aby pokazać, jak można go rozwiązać za pomocą różnych funkcji językowych:
Korzystanie z mapy 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)
Wersja rekurencyjna - odpowiednik mapowania:
Wymaga destrukturyzacji i rozproszenia tablic .
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)
Gdy ostateczna kolejność tablicy nie jest ważna, możesz użyć
object
jako HashMap struktury danych struktury danych. Bardzo przydatne, jeśli masz już kolekcję kluczy jakoobject
- w przeciwnym razie musisz najpierw zmienić swoją reprezentację.Wymaga rozproszenia reszty obiektu , obliczonych nazw właściwości i 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)
źródło
Jeśli próbujesz tylko zamienić jedną nieruchomość, lodash
_.find
i_.set
powinno wystarczyć:var arr = [{id: 1, name: "Person 1"}, {id: 2, name: "Person 2"}]; _.set(_.find(arr, {id: 1}), 'name', 'New Person');
źródło
Jeśli punkt wstawiania nowego obiektu nie musi pasować do indeksu poprzedniego obiektu, najprostszym sposobem na zrobienie tego z lodash jest użycie,
_.reject
a następnie wpychanie nowych wartości do tablicy: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" }]
Jeśli masz wiele wartości, które chcesz zastąpić w jednym przebiegu, możesz wykonać następujące czynności (zapisane w formacie innym niż 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" }]
źródło
var arr= [{id: 1, name: "Person 1"}, {id:2, name:"Person 2"}]; var index = _.findIndex(arr, {id: 1}); arr[index] = {id: 100, name: 'xyz'}
źródło
Używając funkcji lodash unionWith, możesz wykonać prosty skok do obiektu. Dokumentacja stwierdza, że jeśli istnieje dopasowanie, użyje pierwszej tablicy. Zawiń zaktualizowany obiekt w [] (tablica) i umieść go jako pierwszą tablicę funkcji unii. Po prostu określ swoją pasującą logikę, a jeśli zostanie znaleziona, zastąpi ją, a jeśli nie, doda ją
Przykład:
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)
źródło
Niezły wariant)
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';
źródło
Przeszedłem też przez to i zrobiłem to po prostu w ten sposób.
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 ))
Jeśli chcemy, możemy to uogólnić
const replaceWhere = (list, predicate, replacement) => { return list.map(item => predicate(item) ? replacement : item) } replaceWhere(persons, person => person.id === updatedPerson.id, updatedPerson)
źródło
Jeśli szukasz sposobu na niezmienną zmianę kolekcji (tak jak ja, kiedy znalazłem twoje pytanie), możesz rzucić okiem na niezmienność-pomocnika , bibliotekę rozwidloną z oryginalnego narzędzia React. W twoim przypadku osiągnąłbyś to, o czym wspomniałeś w następujący sposób:
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"}]
źródło
Możesz to zrobić bez użycia 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")
źródło
Niezmienny , odpowiedni dla
ReactJS
:Założyć:
cosnt arr = [{id: 1, name: "Person 1"}, {id:2, name:"Person 2"}];
Zaktualizowana pozycja jest drugą, a nazwa zmienia się na
Special Person
:const updatedItem = {id:2, name:"Special Person"};
Podpowiedź : lodash ma przydatne narzędzia, ale teraz mamy niektóre z nich w Ecmascript6 +, więc po prostu używam
map
funkcji, która istnieje na obulodash
iecmascript6+
:const newArr = arr.map(item => item.id === 2 ? updatedItem : item);
źródło