Есть функция в lodash для замены совпадающего элемента
Интересно, есть ли более простой метод в lodash для замены элемента в коллекции JavaScript? (Возможно дублировать, но я не понял ответа там:)
Я просмотрел их документацию, но ничего не нашел
Мой код:
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:
Объект, который я пытаюсь заменить, имеет много свойств, таких как
{id: 1, Prop1:..., Prop2:... и т.д.}
Решение:
Благодаря dfsq, но я нашел правильное решение в рамках lodash, которое, кажется, работает нормально и довольно аккуратно, и я добавил его в mixin, так как я Это требование во многих местах. 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));
Быстрое решение
Как указано в @dfsq, следующий путь быстрее
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);
}
};
Ответы
Ответ 1
В вашем случае все, что вам нужно сделать, это найти объект в массиве и использовать метод Array.prototype.splice
:
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>
Ответ 2
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);
}
Теперь пусть тестовая производительность для всех методов:
// TC 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 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>
Ответ 3
Вы также можете использовать findIndex и выбрать для достижения того же результата:
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);
}
Ответ 4
Похоже, самое простое решение - использовать ES6 .map
или 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;
});
Это имеет хороший эффект, чтобы избежать мутирования исходного массива.
Ответ 5
Если точке вставки нового объекта не нужно соответствовать предыдущему объекту, тогда самый простой способ сделать это с помощью lodash - это _.reject
, а затем нажатие новых значений в массив:
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" }]
Если у вас есть несколько значений, которые вы хотите заменить за один проход, вы можете сделать следующее (записано в формате, отличном от 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" }]
Ответ 6
С течением времени вы должны принять более функциональный подход, в котором вы должны избегать мутаций данных и писать небольшие функции единой ответственности. В стандарте ECMAScript 6 вы можете пользоваться парадигмой функционального программирования в JavaScript с помощью методов map
, filter
и reduce
. Вам не нужен другой lodash, подчеркивание или что еще делать самые простые вещи.
Внизу ниже я включил некоторые предлагаемые решения этой проблемы, чтобы показать, как эта проблема может быть решена с использованием различных языковых возможностей:
Использование карты 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)
Ответ 7
Если вы ищете способ неизменно менять коллекцию (как я был, когда я нашел ваш вопрос), вы можете взглянуть на immutability-helper, библиотека, раздвоенная из оригинальной утилиты React. В вашем случае вы выполните следующее:
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"}]