web-dev-qa-db-ja.com

存在する場合は更新するか、オブジェクトの配列に新しい要素を追加します-JavaScript + Lodashのエレガントな方法

だから私はそのようなオブジェクトの配列を持っています:

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のようなものを考えていました。

18
ganqqwerty

配列の代わりにオブジェクトを使用できます

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

キーはuidsです。関数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を見てください(オブジェクト指向のソリューション here =)

13
friedi

最初のアプローチでは、 findIndex() のおかげでLodashは必要ありません:

_function addOrReplace(array, item) { // (1)
  const i = array.findIndex(_item => _item.id === item.id);
  if (i > -1) array[i] = item; // (2)
  else array.Push(item);
}
_

例:

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

addOrReplace(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'}
]
*/

addOrReplace(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)その他の可能な名前:addOrUpdate()upsert()appendOrUpdate()insertOrUpdate()...

(2)はarray.splice(i, 1, item)でも実行できます

このアプローチは「変更可能」(対「不変」)であることに注意してください。つまり、(元の配列に手を加えることなく)新しい配列を返す代わりに、元の配列を直接変更します。ほとんどの場合、これはあなたが望むものです。

12
tanguy_k

古い投稿ですが、フィルター機能を使用しないのはなぜですか?

// 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 の仕組みを示しています。

4
Chris Sullivan

多分

_.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");
3
Tomalak

私は個人的に元の配列/オブジェクトを変更するソリューションが好きではないので、これは私がやったことです:

_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機能が含まれています。

2
pgarciacamou

配列のインデックスをuid?と同じにするのはどうですか?

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

そうすれば、単に使用することができます

arr[affectedId] = changedObject;
1
Drecker