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

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

本文关键字:javascript 元素 lodash 方式 更新 存在 新元素 添加 数组 对象 如果      更新时间:2023-09-26

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

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

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

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

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

我正在使用 lodash,所以我在考虑诸如带有自定义相等性检查的修改_.union之类的东西。

在您的第一种方法中,由于findIndex(),不需要 Lodash :

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

例:

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(其他可能的名称:addOrReplace()addOrUpdate()appendOrUpdate()insertOrUpdate()...

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

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

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

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

键是 uid。现在你的函数addOrReplace很简单,如下所示:

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

更新

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

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

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

如果您不介意最后项目的顺序,那么更简洁的功能 es6 方法如下:

function addOrReplace(arr, newObj){ 
 return [...arr.filter((obj) => obj.uid !== newObj.uid), {...newObj}];
}
// or shorter one line version 
const addOrReplace = (arr, newObj) => [...arr.filter((o) => o.uid !== newObj.uid), {...newObj}];
如果项目

存在,它将被排除,然后在末尾添加新项目,基本上它是替换的,如果项目没有找到,将在最后添加新对象。

这样,您将拥有不可变的列表。唯一要知道的是,例如,如果您在屏幕上呈现列表,则需要进行某种排序以保持列表顺序。

希望这对某人来说很方便。

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

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

你会像这样使用它:

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

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

  1. 通过使用lodash -> _.findIndex()谓词可以是多个事物
  2. 通过传递回调getItem(),您可以决定是完全替换项目还是进行一些修改,就像我在示例中所做的那样。

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

<小时 />

对此还有第二种方法。我们可以使用 JavaScript Map 对象,它"保存键值对并记住键的原始插入顺序"加上"任何值(对象和基元值(都可以用作键或值"。

let myMap = new Map(
  ['1', { id: '1', first: true }] // key-value entry
  ['2', { id: '2', second: true }]
)
myMap = new Map([
  ...myMap, 
  ['1', { id: '1', first: true, other: '...' }]
  ['3', { id: '3', third: true }]
])

myMap将按顺序包含以下条目:

['1', { id: '1', first: true, other: '...' }]
['2', { id: '2', second: true }]
['3', { id: '3', third: true }]

我们可以利用地图的这个特性来添加或替换其他元素:

function addOrReplaceBy(array, value, key = "id") {
  return Array.from(
    new Map([
      ...array.map(item => [ item[key], item ]),
      [value[key], value]
    ]).values()
  )
}

也许

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

var elem = {uid: 3, name: 'new element name name', description: "cocoroco"};
_.mergeById(arr, elem, "uid");

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

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

这是一个显示它如何工作的jsfiddle。

非常复杂的解决方案:D这是一个行:

const newArray = array.filter(obj => obj.id !== newObj.id).concat(newObj)
如果数组

的索引与uid相同呢?,例如:

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

这样你就可以简单地使用

arr[affectedId] = changedObject;

Backbone.Collection恰恰提供了此功能。尽可能节省自己的精力!

var UidModel = Backbone.Model.extend({
    idAttribute: 'uid'
});
var data = new Backbone.Collection([
    {uid: 1, name: "bla", description: "cucu"},
    {uid: 2, name: "smth else", description: "cucarecu"}
], {
    model: UidModel
});
data.add({uid: 1, name: 'changed name', description: "changed description"}, {merge: true});
data.add({uid: 3, name: 'new element name name', description: "cocoroco"});
console.log(data.toJSON());