web-dev-qa-db-fra.com

Mettre à jour s'il existe ou ajouter un nouvel élément au tableau d'objets - manière élégante en javascript + lodash

J'ai donc un tableau d'objets comme ça:

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

uid est l'identifiant unique de l'objet dans ce tableau. Je cherche la manière élégante de modifier l'objet si nous avons l'objet avec le uid, Donné ou d'ajouter un nouvel élément, si le uid présenté n'existe pas dans le tableau. J'imagine que la fonction doit se comporter comme ça dans la console 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"}
]

Ma façon actuelle ne semble pas très élégante et fonctionnelle:

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

J'utilise lodash, donc je pensais à quelque chose comme _.union Modifié avec un contrôle d'égalité personnalisé.

18
ganqqwerty

Vous pouvez utiliser un objet au lieu d'un tableau :

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

Les clés sont les uids . Maintenant, votre fonction addOrReplace est simple comme ceci:

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

[~ # ~] mise à jour [~ # ~]

Il est également possible d'utiliser un objet comme index en plus du tableau.
De cette façon, vous avez des recherches rapides ainsi qu'un tableau fonctionnel:

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

Jetez un oeil à la jsfiddle-demo (une solution orientée objet que vous trouverez ici =)

13
friedi

Dans votre première approche, pas besoin de Lodash grâce à findIndex() :

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

Exemple:

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) autres noms possibles: addOrUpdate(), upsert(), appendOrUpdate(), insertOrUpdate()...

(2) peut également être fait avec array.splice(i, 1, item)

Notez que cette approche est "mutable" (vs "immuable"): cela signifie qu'au lieu de retourner un nouveau tableau (sans toucher le tableau d'origine), il modifie directement le tableau d'origine. La plupart du temps, c'est ce que vous voulez.

12
tanguy_k

Ancien poste, mais pourquoi ne pas utiliser la fonction de filtrage?

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

Voici un jsfiddle montrant comment cela fonctionne.

4
Chris Sullivan

Peut être

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

et

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

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

Personnellement, je n'aime pas les solutions qui modifient le tableau/objet d'origine, c'est donc ce que j'ai fait:

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

Et vous l'utiliseriez comme:

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

J'ai décidé de le rendre plus flexible:

  1. En utilisant lodash -> _.findIndex(), le prédicat peut être multiple
  2. En passant un rappel getItem(), vous pouvez décider de remplacer complètement l'élément ou de faire quelques modifications, comme je l'ai fait dans mon exemple.

Remarque: cette solution contient certaines fonctionnalités ES6 telles que la déstructuration, les fonctions flèches, entre autres.

2
pgarciacamou

Et si les index du tableau étaient identiques à ceux de uid?, Comme:

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

de cette façon, vous pouvez simplement utiliser

arr[affectedId] = changedObject;
1
Drecker