是否有一个函数来替换匹配的项目

我想知道是否有一种更简单的方法在 loash 中替换 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,但我发现了一个适当的解决方案,在 loash,似乎工作很好,是相当整洁,我把它在一个混合以及,因为我有这个要求在许多地方。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);
}
};
204483 次浏览

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

您还可以使用 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);
}
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或者 loash 的 _.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;
});

这样做的好处是避免了对原始数组进行变异。

如果新对象的插入点不需要与前一个对象的索引匹配,那么使用 loash 的最简单方法是使用 _.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"}]

随着时间的推移,您应该采用一种功能性更强的方法,其中应该避免数据突变,并编写小型的单一职责函数。使用 ECmascript 6标准,你可以使用提供的 mapfilterreduce方法来享受 JavaScript 的功能编程范型。你不需要另一个浪费,下划线或其他什么做最基本的事情。

下面我列出了一些针对这个问题的建议解决方案,以说明如何利用不同的语言特点来解决这个问题:

使用 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-否则你必须改变你的表示第一。

需要 物体静止展开物体静止展开物计算属性名计算属性名Object.entry

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)

如果你只是想替换一个属性,loash _.find_.set应该足够了:

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


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

[ ES6] 这个代码对我有用。

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

您可以不使用 loash 做到这一点。

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

使用 lodashunionWith 函数,您可以完成一个对象的简单的 upsert。文档指出,如果有匹配,它将使用第一个数组。将更新后的对象包装在[](array)中,并将其作为 union 函数的第一个数组。只需指定您的匹配逻辑,如果找到它将替换它,如果找不到它将添加它

例如:

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)

也是不错的变体)

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';
var arr= [{id: 1, name: "Person 1"}, {id:2, name:"Person 2"}];
var index = _.findIndex(arr, {id: 1});
arr[index] = {id: 100, name: 'xyz'}

不变 ,适用于 ReactJS:

假设:

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

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

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

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

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

我也遇到过这种情况,而且就是这么简单。

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)

如果你想创建一个函数并保持它为“ loash-ey”,你可以创建一个可以使用回调函数的包装函式。它使得该函数更具有通用性。

要写这个,可以试试这样写

function findAllAndReplace(array, replacement, callback){
return array.map( element => callback(element) ? replacement : element )
}

要通过键查找和替换,只需使您的回调非常简单

但是如果您想要更高级的功能,那么几乎不需要付出任何额外的努力就可以将其合并。这里有一些例子。

  1. (Simple)查找 id 为2的项,将其替换为 id: 7
const items = [{id: 1}, {id: 2}, {id: 3}, {id: 4}, {id: 5}]


findAllAndReplace( items, {id: 7}, item => item.id === 2 )
  1. (稍微复杂一点)找到28岁的约翰,用一个28岁的约翰取代他

const people = [
{
name: "John",
age: 20
},
{
name: "John",
age: 28
},
{
name: "Jim",
age: 28
},
]




findAllAndReplace(
people, // all the people
{ name: "Jon", age: 28 }, // Replacement value
(person) => person.name === "jon" && person.age === 21 // callback function
)

此外,上面的方法将找到所有匹配并替换它们的实例,但是如果您只想为其中一个实例执行此操作,则可以执行下面的操作。

function findOneAndReplace(array, replacement, callback){
const splitIndex = array.findIndex(callback)
    

// This if statement can be ommitted, but might
// be handy depending on your use case
if(splitIndex < 0){
throw new Error("Swap Element not found")
}


const leadingTerms = array.slice(0, splitIndex)
const trailingTerms = array.slice(splitIndex + 1, array.length)
return [...leadingTerms, replacement, ...trailingTerms]
)

注意: 如果函数没有找到匹配的元素,那么让函数中断可能是有用的,但是如果你不想要这个特性,你可以删除这些代码行。