web-dev-qa-db-fra.com

y a-t-il une fonction dans lodash pour remplacer l'élément correspondant

Je me demande s'il existe une méthode plus simple dans lodash pour remplacer un élément dans une collection JavaScript? (Possible dupliquer mais je n'ai pas compris la réponse ici :)

J'ai regardé leur documentation mais je n'ai rien trouvé

Mon code est:

var arr = [{id: 1, name: "Person 1"}, {id:2, name:"Person 2"}];
// Can following code be reduced to something like _.XX(arr, {id:1}, {id:1, name: "New Name"});
_.each(arr, function(a, idx){
  if(a.id === 1){
    arr[idx] = {id:1, name: "Person New Name"};
    return false;
  }
});

_.each(arr, function(a){
  document.write(a.name);
});

Update: L'objet que je tente de remplacer a de nombreuses propriétés telles que

{id: 1, Prop1: ..., Prop2: ..., etc.}

Solution:

Merci à dfsq mais j’ai trouvé une solution appropriée dans lodash qui semble bien fonctionner et qui est plutôt soignée et que j’ai mise dans un mix aussi bien puisque cette exigence existe à de nombreux endroits. JSBin

var update = function(arr, key, newval) {
  var match = _.find(arr, key);
  if(match)
    _.merge(match, newval);
  else
    arr.Push(newval);    
};

_.mixin({ '$update': update });

var arr = [{id: 1, name: "Person 1"}, {id:2, name:"Person 2"}];

_.$update(arr, {id:1}, {id:1, name: "New Val"});


document.write(JSON.stringify(arr));

Solution plus rapide Comme l'a souligné @dfsq, le suivi est beaucoup plus rapide

var upsert = function (arr, key, newval) {
    var match = _.find(arr, key);
    if(match){
        var index = _.indexOf(arr, _.find(arr, key));
        arr.splice(index, 1, newval);
    } else {
        arr.Push(newval);
    }
};
90
Vishal Seth

Dans votre cas, tout ce que vous avez à faire est de trouver un objet dans un tableau et d'utiliser la méthode Array.prototype.splice:

var arr = [{id: 1, name: "Person 1"}, {id:2, name:"Person 2"}];

// Find item index using _.findIndex (thanks @AJ Richardson for comment)
var index = _.findIndex(arr, {id: 1});

// Replace item at index using native splice
arr.splice(index, 1, {id: 100, name: 'New object.'});

// "console.log" result
document.write(JSON.stringify( arr ));
<script src="//cdnjs.cloudflare.com/ajax/libs/lodash.js/2.4.1/lodash.min.js"></script>

130
dfsq
function findAndReplace(arr, find, replace) {
  let i;
  for(i=0; i < arr.length && arr[i].id != find.id; i++) {}
  i < arr.length ? arr[i] = replace : arr.Push(replace);
}

Maintenant testons les performances de toutes les méthodes:

// TC's first approach
function first(arr, a, b) {
  _.each(arr, function (x, idx) {
    if (x.id === a.id) {
      arr[idx] = b;
      return false;
    }
  });
}

// solution with merge
function second(arr, a, b) {
  const match = _.find(arr, a);
  if (match) {
    _.merge(match, b);
  } else {
    arr.Push(b);
  }
}

// most voted solution
function third(arr, a, b) {
  const match = _.find(arr, a);
  if (match) {
    var index = _.indexOf(arr, _.find(arr, a));
    arr.splice(index, 1, b);
  } else {
    arr.Push(b);
  }
}

// my approach
function fourth(arr, a, b){
  let l;
  for(l=0; l < arr.length && arr[l].id != a.id; l++) {}
  l < arr.length ? arr[l] = b : arr.Push(b);
}

function test(fn, times, el) {
  const arr = [], size = 250;
  for (let i = 0; i < size; i++) {
    arr[i] = {id: i, name: `name_${i}`, test: "test"};
  }

  let start = Date.now();
  _.times(times, () => {
    const id = Math.round(Math.random() * size);
    const a = {id};
    const b = {id, name: `${id}_name`};
    fn(arr, a, b);
  });
  el.innerHTML = Date.now() - start;
}

test(first, 1e5, document.getElementById("first"));
test(second, 1e5, document.getElementById("second"));
test(third, 1e5, document.getElementById("third"));
test(fourth, 1e5, document.getElementById("fourth"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.14.1/lodash.min.js"></script>
<div>
  <ol>
    <li><b id="first"></b> ms [TC's first approach]</li>
    <li><b id="second"></b> ms [solution with merge]</li>
    <li><b id="third"></b> ms [most voted solution]</li>
    <li><b id="fourth"></b> ms [my approach]</li>
  </ol>
<div>

18
evilive

On dirait que la solution la plus simple consiste à utiliser le .map de ES6 ou le _.map de lodash:

var arr = [{id: 1, name: "Person 1"}, {id: 2, name: "Person 2"}];

// lodash
var newArr = _.map(arr, function(a) {
  return a.id === 1 ? {id: 1, name: "Person New Name"} : a;
});

// ES6
var newArr = arr.map(function(a) {
  return a.id === 1 ? {id: 1, name: "Person New Name"} : a;
});

Cela a pour effet d’éviter la mutation du tableau d’origine.

16
Spencer

[ES6] Ce code fonctionne pour moi. 

let result = array.map(item => item.id === updatedItem.id ? updatedItem : item)
13
shebik

Vous pouvez également utiliser findIndex et pick pour obtenir le même résultat:

  var arr  = [{id: 1, name: "Person 1"}, {id:2, name:"Person 2"}];
  var data = {id: 2, name: 'Person 2 (updated)'};
  var index = _.findIndex(arr, _.pick(data, 'id'));
  if( index !== -1) {
    arr.splice(index, 1, data);
  } else {
    arr.Push(data);
  }
9
JVitela

À mesure que le temps passe, vous devriez adopter une approche plus fonctionnelle dans laquelle vous devriez éviter les mutations de données et écrire de petites fonctions à responsabilité unique. Avec le standard ECMAScript 6, vous pouvez utiliser le paradigme de programmation fonctionnelle en JavaScript avec les méthodes map, filter et reduce fournies. Vous n'avez pas besoin d'un autre lodash, d'un trait de soulignement ou de quoi faire d'autre pour effectuer des tâches de base. 

En bas, j'ai inclus quelques solutions proposées à ce problème afin de montrer comment ce problème peut être résolu en utilisant différentes fonctionnalités de langage: 

Utilisation de la carte ES6:

const replace = predicate => replacement => element =>
  predicate(element) ? replacement : element
 
const arr = [ { id: 1, name: "Person 1" }, { id:2, name:"Person 2" } ];
const predicate = element => element.id === 1
const replacement = { id: 100, name: 'New object.' }

const result = arr.map(replace (predicate) (replacement))
console.log(result)


Version récursive - équivalent du mappage:

Requiert déstructuration et propagation du tableau.

const replace = predicate => replacement =>
{
  const traverse = ([head, ...tail]) =>
    head
    ? [predicate(head) ? replacement : head, ...tail]
    : []
  return traverse
}
 
const arr = [ { id: 1, name: "Person 1" }, { id:2, name:"Person 2" } ];
const predicate = element => element.id === 1
const replacement = { id: 100, name: 'New object.' }

const result = replace (predicate) (replacement) (arr)
console.log(result)


Lorsque l'ordre du tableau final n'est pas important, vous pouvez utiliser une object en tant que structure HashMap data. Très pratique si vous avez déjà saisi la collecte en tant que object - sinon vous devez d'abord modifier votre représentation.

Requiert reste de l'objet spread , noms de propriété calculés et Object.entries.

const replace = key => ({id, ...values}) => hashMap =>
({
  ...hashMap,       //original HashMap
  [key]: undefined, //delete the replaced value
  [id]: values      //assign replacement
})

// HashMap <-> array conversion
const toHashMapById = array =>
  array.reduce(
    (acc, { id, ...values }) => 
    ({ ...acc, [id]: values })
  , {})
  
const toArrayById = hashMap =>
  Object.entries(hashMap)
  .filter( // filter out undefined values
    ([_, value]) => value 
  ) 
  .map(
    ([id, values]) => ({ id, ...values })
  )

const arr = [ { id: 1, name: "Person 1" }, { id:2, name:"Person 2" } ];
const replaceKey = 1
const replacement = { id: 100, name: 'New object.' }

// Create a HashMap from the array, treating id properties as keys
const hashMap = toHashMapById(arr)
console.log(hashMap)

// Result of replacement - notice an undefined value for replaced key
const resultHashMap = replace (replaceKey) (replacement) (hashMap)
console.log(resultHashMap)

// Final result of conversion from the HashMap to an array
const result = toArrayById (resultHashMap)
console.log(result)

4

Vous pouvez le faire sans utiliser lodash.

let arr = [{id: 1, name: "Person 1"}, {id: 2, name: "Person 2"}];
let newObj = {id: 1, name: "new Person"}

/*Add new prototype function on Array class*/
Array.prototype._replaceObj = function(newObj, key) {
  return this.map(obj => (obj[key] === newObj[key] ? newObj : obj));
};

/*return [{id: 1, name: "new Person"}, {id: 2, name: "Person 2"}]*/
arr._replaceObj(newObj, "id") 
1
Sunny

Si le point d'insertion du nouvel objet n'a pas besoin de correspondre à l'index de l'objet précédent, le moyen le plus simple de procéder avec lodash consiste à utiliser _.reject puis à insérer de nouvelles valeurs dans le tableau:

var arr = [
  { id: 1, name: "Person 1" }, 
  { id: 2, name: "Person 2" }
];

arr = _.reject(arr, { id: 1 });
arr.Push({ id: 1, name: "New Val" });

// result will be: [{ id: 2, name: "Person 2" }, { id: 1, name: "New Val" }]

Si vous souhaitez remplacer plusieurs valeurs en une seule fois, vous pouvez effectuer les opérations suivantes (écrites au format non ES6):

var arr = [
  { id: 1, name: "Person 1" }, 
  { id: 2, name: "Person 2" }, 
  { id: 3, name: "Person 3" }
];

idsToReplace = [2, 3];
arr = _.reject(arr, function(o) { return idsToReplace.indexOf(o.id) > -1; });
arr.Push({ id: 3, name: "New Person 3" });
arr.Push({ id: 2, name: "New Person 2" });


// result will be: [{ id: 1, name: "Person 1" }, { id: 3, name: "New Person 3" }, { id: 2, name: "New Person 2" }]
1
richt

En utilisant la fonction lodash unionWith, vous pouvez effectuer une simple conversion vers un objet. La documentation indique que s'il y a correspondance, elle utilisera le premier tableau. Enveloppez votre objet mis à jour dans [] (tableau) et placez-le comme premier tableau de la fonction d'union. Spécifiez simplement votre logique de correspondance et si elle est trouvée, elle la remplacera et sinon, elle l'ajoutera. 

Exemple:

let contacts = [
     {type: 'email', desc: 'work', primary: true, value: 'email prim'}, 
     {type: 'phone', desc: 'cell', primary: true, value:'phone prim'},
     {type: 'phone', desc: 'cell', primary: false,value:'phone secondary'},
     {type: 'email', desc: 'cell', primary: false,value:'email secondary'}
]

// Update contacts because found a match
_.unionWith([{type: 'email', desc: 'work', primary: true, value: 'email updated'}], contacts, (l, r) => l.type == r.type && l.primary == r.primary)

// Add to contacts - no match found
_.unionWith([{type: 'fax', desc: 'work', primary: true, value: 'fax added'}], contacts, (l, r) => l.type == r.type && l.primary == r.primary)
0
Jeffrey

Si vous essayez simplement de remplacer une propriété, lodash _.find et _.set devraient suffire:

var arr = [{id: 1, name: "Person 1"}, {id: 2, name: "Person 2"}];

_.set(_.find(arr, {id: 1}), 'name', 'New Person');
0
Andrei Gavrilov

Pas mauvaise variante aussi)

var arr = [{id: 1, name: "Person 1"}, {id: 2, name: "Person 2"}];

var id = 1; //id to find

arr[_.find(arr, {id: id})].name = 'New Person';
0
Ivan Pirus

Si vous cherchez un moyen de changer immuablement la collection (comme je l'étais quand j'ai trouvé votre question), vous pouvez jeter un oeil à immutability-helper , une bibliothèque créée à partir de React util. Dans votre cas, vous accomplissez ce que vous avez mentionné via:

var update = require('immutability-helper')
var arr = [{id: 1, name: "Person 1"}, {id:2, name:"Person 2"}]
var newArray = update(arr, { 0: { name: { $set: 'New Name' } } })
//=> [{id: 1, name: "New Name"}, {id:2, name:"Person 2"}]
0
Aaron_H