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

如果存在,则更新或向对象数组中添加新元素-javascript中的优雅方式+;洛达斯,javascript,arrays,underscore.js,lodash,Javascript,Arrays,Underscore.js,Lodash,所以我有一个这样的对象数组: var arr = [ {uid: 1, name: "bla", description: "cucu"}, {uid: 2, name: "smth else", description: "cucarecu"}, ] uid是此数组中对象的唯一id。如果对象具有给定的uid,或添加新元素(如果数组中不存在呈现的uid),我正在寻找一种优雅的方法来修改对象。我想象函数在js控制台中的行为如下: > addOrReplace(arr, {uid:

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

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,所以我想到了类似于使用自定义等式检查修改的
\uu.union

让数组的索引与
uid
相同怎么样,比如:

arr = [];
arr[1] = {uid: 1, name: "bla", description: "cucu"};
arr[2] = {uid: 2, name: "smth else", description: "cucarecu"};
这样你就可以简单地使用

arr[affectedId] = changedObject;

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

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;
}
看一看(你会发现一个面向对象的解决方案)

也许吧

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


老帖子,但为什么不使用过滤功能呢

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

以下是它的工作原理。

在您的第一种方法中,由于以下原因,无需使用Lodash:

例如:

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) 也可以使用阵列拼接(i,1,项)


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

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

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
  })
);
我决定做的是让它更灵活:

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

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

    如果我能保证uuid始终为int,则效果会很好。我认为有时情况并非如此。但是,即使我有这样的保证,我也会对数组中缺少这样的元素感到不舒服:
    arr[2134],arr[2135]
    ,而arr[0]不存在。@ganqwerty所以不用传统的数组而使用关联数组(object),这样你就可以使用非整数索引,并且在数组中没有“洞”,我也喜欢这种结构,但是我使用的一些库不支持它。例如,它只能遍历数组。您只能将此结构用作索引。通过这种方式,您可以快速查找,并且需要一个额外的数组,例如数组。向上投票!有两个问题,我需要根据时间戳按顺序排序键,并且我需要能够以数组方式访问所有项以计算统计度量。这是第一次失败
    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
      })
    );