如果存在则更新或向对象数组添加新元素 - javascript + lodash 中的优雅方式

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/25764719/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-22 21:50:49  来源:igfitidea点击:

Update if exists or add new element to array of objects - elegant way in javascript + lodash

javascriptarraysunderscore.jslodash

提问by ganqqwerty

So I have an array of objects like that:

所以我有一个这样的对象数组:

var arr = [
  {uid: 1, name: "bla", description: "cucu"},
  {uid: 2, name: "smth else", description: "cucarecu"},
]

uidis unique id of the object in this array. I'm searching for the elegant way to modify the object if we have the object with the given uid,or add a new element, if the presented uiddoesn't exist in the array. I imagine the function to be behave like that in js console:

uid是此数组中对象的唯一 ID。我正在寻找一种优雅的方式来修改对象,如果我们有给定的对象uid,或添加一个新元素,如果uid数组中不存在呈现的元素。我想象这个函数在 js 控制台中的行为是这样的:

> addOrReplace(arr, {uid: 1, name: 'changed name', description: "changed description"})
> arr
[
  {uid: 1, name: "bla", description: "cucu"},
  {uid: 2, name: "smth else", description: "cucarecu"},
]
> addOrReplace(arr, {uid: 3, name: 'new element name name', description: "cocoroco"})
> arr
[
  {uid: 1, name: "bla", description: "cucu"},
  {uid: 2, name: "smth else", description: "cucarecu"},
  {uid: 3, name: 'new element name name', description: "cocoroco"}
]

My current way doesn't seem to be very elegant and functional:

我目前的方式似乎不是很优雅和实用:

function addOrReplace (arr, object) {
  var index = _.findIndex(arr, {'uid' : object.uid});
  if (-1 === index) {
    arr.push(object);
  } else {
    arr[index] = object;
  }
} 

I'm using lodash, so I was thinking of something like modified _.unionwith custom equality check.

我正在使用 lodash,所以我正在考虑_.union使用自定义相等检查进行修改。

采纳答案by friedi

You can use an object instead of an array:

您可以使用对象而不是数组

var hash = {
  '1': {uid: 1, name: "bla", description: "cucu"},
  '2': {uid: 2, name: "smth else", description: "cucarecu"}
};

The keys are the uids. Now your function addOrReplaceis simple like this:

键是 uids。现在你的函数addOrReplace很简单:

function addOrReplace(hash, object) {
    hash[object.uid] = object;
}


UPDATE

更新

It's also possible to use an object as an indexin addition to the array.
This way you've got fast lookupsand also a working array:

除了数组之外,还可以使用对象作为索引
通过这种方式,您可以获得快速查找和工作数组:

var arr = [],
    arrIndex = {};

addOrReplace({uid: 1, name: "bla", description: "cucu"});
addOrReplace({uid: 2, name: "smth else", description: "cucarecu"});
addOrReplace({uid: 1, name: "bli", description: "cici"});

function addOrReplace(object) {
    var index = arrIndex[object.uid];
    if(index === undefined) {
        index = arr.length;
        arrIndex[object.uid] = index;
    }
    arr[index] = object;
}

Take a look at the jsfiddle-demo(an object-oriented solution you'll find here)

看看jsfiddle-demo(你可以在这里找到一个面向对象的解决方案)

回答by tanguy_k

In your first approach, no need for Lodash thanks to findIndex():

在您的第一种方法中,由于以下原因,不需要 Lodash findIndex()

function upsert(array, item) { // (1)
  const i = array.findIndex(_item => _item.id === item.id);
  if (i > -1) array[i] = item; // (2)
  else array.push(item);
}

Example:

例子:

const array = [
  {id: 0, name: 'Apple', description: 'fruit'},
  {id: 1, name: 'Banana', description: 'fruit'},
  {id: 2, name: 'Tomato', description: 'vegetable'}
];

upsert(array, {id: 2, name: 'Tomato', description: 'fruit'})
console.log(array);
/* =>
[
  {id: 0, name: 'Apple', description: 'fruit'},
  {id: 1, name: 'Banana', description: 'fruit'},
  {id: 2, name: 'Tomato', description: 'fruit'}
]
*/

upsert(array, {id: 3, name: 'Cucumber', description: 'vegetable'})
console.log(array);
/* =>
[
  {id: 0, name: 'Apple', description: 'fruit'},
  {id: 1, name: 'Banana', description: 'fruit'},
  {id: 2, name: 'Tomato', description: 'fruit'},
  {id: 3, name: 'Cucumber', description: 'vegetable'}
]
*/

(1) other possible names: addOrReplace(), addOrUpdate(), appendOrUpdate(), insertOrUpdate()...

(1) 其他可能的名称:addOrReplace(), addOrUpdate(), appendOrUpdate(), insertOrUpdate()...

(2) can also be done with array.splice(i, 1, item)

(2) 也可以用 array.splice(i, 1, item)

Note that this approach is "mutable" (vs "immutable"): it means instead of returning a new array (without touching the original array), it modifies directly the original array.

请注意,这种方法是“可变的”(相对于“不可变的”):它意味着不是返回一个新数组(不接触原始数组),而是直接修改原始数组。

回答by Tomalak

Maybe

也许

_.mixin({
    mergeById: function mergeById(arr, obj, idProp) {
        var index = _.findIndex(arr, function (elem) {
            // double check, since undefined === undefined
            return typeof elem[idProp] !== "undefined" && elem[idProp] === obj[idProp];
        });

        if (index > -1) {
            arr[index] = obj; 
        } else {
            arr.push(obj);
        }

        return arr;
    }
});

and

var elem = {uid: 3, name: 'new element name name', description: "cocoroco"};

_.mergeById(arr, elem, "uid");

回答by Chris Sullivan

Old post, but why not use the filter function?

旧帖子,但为什么不使用过滤器功能?

// If you find the index of an existing uid, save its index then delete it
//      --- after the filter add the new object.
function addOrReplace( argh, obj ) {
  var index = -1;
  argh.filter((el, pos) => {
    if( el.uid == obj.uid )
      delete argh[index = pos];
    return true;
  });

  // put in place, or append to list
  if( index == -1 ) 
    argh.push(obj);
  else 
    argh[index] = obj;
}

Here is a jsfiddleshowing how it works.

这是一个jsfiddle,展示了它是如何工作的。

回答by pgarciacamou

I personally do not like solutions that modify the original array/object, so this is what I did:

我个人不喜欢修改原始数组/对象的解决方案,所以这就是我所做的:

function addOrReplaceBy(arr = [], predicate, getItem) {
  const index = _.findIndex(arr, predicate);
  return index === -1
    ? [...arr, getItem()]
    : [
      ...arr.slice(0, index),
      getItem(arr[index]),
      ...arr.slice(index + 1)
    ];
}

And you would use it like:

你会像这样使用它:

var stuff = [
  { id: 1 },
  { id: 2 },
  { id: 3 },
  { id: 4 },
];

var foo = { id: 2, foo: "bar" };
stuff = addOrReplaceBy(
  stuff,
  { id: foo.id },
  (elem) => ({
    ...elem,
    ...foo
  })
);

What I decided to do was to make it more flexible:

我决定做的是让它更灵活:

  1. By using lodash -> _.findIndex(), the predicatecan be multiple things
  2. By passing a callback getItem(), you can decide whether to fully replace the item or do some modifications, as I did in my example.
  1. 通过 using lodash -> _.findIndex()谓词可以是多个东西
  2. 通过传递 callback getItem(),您可以决定是完全替换该项目还是进行一些修改,就像我在示例中所做的那样。

Note: this solution contains some ES6 features such as destructuring, arrow functions, among others.

注意:此解决方案包含一些 ES6 特性,例如解构、箭头函数等。

回答by Drecker

What about having the indexes of the array same as the uid?, like:

使数组的索引与uid?相同怎么样,例如:

arr = [];
arr[1] = {uid: 1, name: "bla", description: "cucu"};
arr[2] = {uid: 2, name: "smth else", description: "cucarecu"};

that way you could just simply use

这样你就可以简单地使用

arr[affectedId] = changedObject;