lodash中是否有功能替换匹配项


134

我想知道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中找到了一个合适的解决方案,该解决方案似乎很好用,非常整洁,我也把它放在了mixin中,因为我在很多地方都有这个要求。联合会

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

7
我认为您也可以将match用作“更快的解决方案”的第4行中_.indexOf的第二个参数,无需在那里重新计算该值,这会使事情变得更快。
davertron '16

2
更快:_.findIndex用于比赛。
朱利安·K

1
只是为了扩展@JulianK和@davertron所说的内容,使用_.findIndex代替_.find将使您同时删除第二个_.find_.indexOf。你迭代通过阵列3次,当你需要的是1
贾斯汀·摩根

Answers:


191

在您的情况下,您需要做的就是在数组中找到对象并使用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>


1
那么,您的解决方案将在性能方面付出更多的代价,因为indexOf它将非常快(它将使用本机浏览器Array.prototype.indexOf)。但是无论如何,很高兴找到适合您的解决方案。
dfsq,2014年

14
为什么不使用_.findIndex?然后,您无需使用_.indexOf
AJ理查森

35

似乎最简单的解决方案是使用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;
});

这具有避免变异原始数组的良好效果。


8
但是每次您都在创建一个新的数组。
kboom

3
但是,不创建新数组的唯一替代方法是更改​​现有数组。同样,创建新阵列可能不会对性能产生影响。来自我的支持。
大雄

24

[ES6]此代码对我有用

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

1.您正在创建数组的新实例,因此“替换”项目不是正确的。2. updatedItem如果if数组不包含具有相同项的项,则将丢失id
邪恶的

这是'update'而不是'upsert'的解决方案(问题是“ lodash中是否存在函数来替换MATCHED项”),是的,它创建了数组的副本,因此如果需要使用它,请不要使用它同一阵列(我没有)
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);
}

现在让我们测试所有方法的性能:


6
拒绝投票是因为对人持消极态度(“这是个玩笑”)使他们无法学习。想象一下,如果我以“成为一个聪明的人来结束我的生活,我希望你不要对自己的情绪懒惰,并去思考它”。
Aditya MP

5
我不想伤害任何人,但我想知道比原始主题创建者的方法差的解决方案如何获得如此多的选票。对此投票的人有什么规则?我感到失望的是,人们一味地信任投票最多的答案,并且没有批判性思维。
邪恶的

1
@evilive有效点,但我看不到那些是如何要求您像以前提供答案/投票的每个人都是白痴一样。这个答案的事实部分很棒,其余的几乎没有什么优势。那对任何人都没有帮助。您可以轻松提出自己的观点,而无需过度的情绪反应。
Thor84no

1
值得一提的是,您的解决方案和TC的解决方案仅按ID进行过滤。这是这两个运行速度更快的第一个原因。另外两个允许您传递过滤所需的对象的任何部分,这可能更适合作为upsert函数。
亚兰

10

您还可以使用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);
  }

6

随着时间的流逝,您应该采用一种更具功能性的方法,在这种方法中,应避免数据突变并编写小的,单一职责的函数。与ECMAScript 6个标准,你可以在JavaScript中享受函数式编程范式与所提供的mapfilterreduce方法。您不需要其他破折号,下划线或其他可以执行大多数基本操作的内容。

下面,我提供了一些针对此问题的建议解决方案,以展示如何使用不同的语言功能来解决此问题:

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


如果最终数组的顺序不重要,则可以objectHashMap用作HashMap数据结构。如果您已经将键集合用作,则非常方便object-否则,您必须先更改表示形式。

需要对象剩余散布计算的属性名称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)



1

如果新对象的插入点不需要与先前对象的索引匹配,则使用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" }]

此方法更改了数组排序
sospedra

1

使用lodash unionWith函数,您可以完成对对象的简单追加操作。文档指出,如果存在匹配项,它将使用第一个数组。将更新后的对象包装在[](数组)中,并将其作为并集函数的第一个数组。只需指定您的匹配逻辑,如果找到了它将替换它,否则将添加它

例:

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)



0

如果您正在寻找一种不变地更改集合的方法(就像我发现问题时一样),则可以看看immutability-helper,它是从原始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"}]

0

您无需使用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") 

0

不变的,适用于ReactJS

承担:

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

更新后的项目是第二个,名称更改为Special Person

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

提示 lodash具有有用的工具,但是现在我们在Ecmascript6 +上有一些工具,所以我只使用和map上都存在的函数:lodashecmascript6+

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

0

也碰到了这一点,就这么简单地做到了。

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

如果需要,我们可以将其概括

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

replaceWhere(persons, person => person.id === updatedPerson.id, updatedPerson)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.