есть ли функция в 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);
});

обновление: Объект, который я пытаюсь заменить, имеет много свойств, таких как

{id: 1, Prop1:..., Prop2:..., и ВКЛ}

устранение:

спасибо dfsq но я нашел правильное решение в lodash, которое, кажется, работает нормально и довольно аккуратно, и я положил его в миксин, так как у меня есть это требование во многих местах. 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);
    }
};
10 80

10 ответов:

в вашем случае все, что вам нужно сделать, это найти объект в массив и использовать 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>
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'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>

кажется, что самое простое решение-использовать ES6 .map или лодашь это _.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;
});

Это имеет хороший эффект, избегая мутации исходного массива.

вы также можете использовать findIndex и pick для достижения того же результата:

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

[ES6] этот код работает для меня.

let result = array.map(item => item.id === updatedItem.id ? updatedItem : item)

С течением времени вы должны принять более функциональный подход, в котором вы должны избегать мутаций данных и писать небольшие, одиночные функции ответственности. С помощью стандарта 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)

рекурсивная версия - эквивалент сопоставление:

требует деструктурируется и массив распространения.

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)

когда порядок конечного массива не важен, вы можете использовать object как HashMap структуры данных. Очень удобно, если у вас уже есть коллекция ключей как object - в противном случае вы должны изменить свое представление в первую очередь.

требует объект rest spread,вычисляемые имена свойств и

Если точка вставки нового объекта не должна совпадать с индексом предыдущего объекта, то самый простой способ сделать это с помощью 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" }]

Если вы ищете способ неизменно изменить коллекцию (как я был, когда я нашел ваш вопрос), вы можете взглянуть на неизменность-помощника, библиотека, разветвленная от исходного React util. В вашем случае, вы бы выполнить то, что вы упомянули через следующее:

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

Если вы просто пытаетесь заменить одно свойство, lodash _.find и _.set должно быть достаточно:

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

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

вы можете сделать это без использования 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")