javascript - empty - ¿hay una función en lodash para reemplazar el elemento coincidente
lodash object empty (13)
Me pregunto si hay un método más simple en lodash para reemplazar un elemento en una colección de JavaScript. (Posible duplicate pero no entendí la respuesta allí :)
Miré su documentación pero no pude encontrar nada
Mi código es:
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);
});
Actualización: el objeto con el que intento reemplazar tiene muchas propiedades como
{id: 1, Prop1: ..., Prop2: ..., y así sucesivamente}
Solución:
Gracias a dfsq pero encontré una solución adecuada dentro de lodash que parece funcionar bien y es bastante ordenada y también la puse en un mixin ya que tengo este requisito en muchos lugares. 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));
Solución más rápida Como lo señaló @dfsq, seguir es mucho más rápido
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);
}
};
A medida que pasa el tiempo, debe adoptar un enfoque más funcional en el que debe evitar las mutaciones de datos y escribir pequeñas funciones de responsabilidad única.
Con el estándar ECMAScript 6, puede disfrutar del paradigma de programación funcional en JavaScript con los métodos de
map
,
filter
y
reduce
.
No necesita otro lodash, guión bajo o qué más hacer para hacer las cosas más básicas.
A continuación, he incluido algunas soluciones propuestas para este problema con el fin de mostrar cómo se puede resolver este problema utilizando diferentes características del lenguaje:
Usando el mapa 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)
Versión recursiva - equivalente de mapeo:
Requiere destructuring y dispersión de la matriz .
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)
Cuando el orden de la matriz final no es importante, puede usar un
object
como estructura de datos
HashMap
.
Muy útil si ya tiene una colección con clave como
object
; de lo contrario, primero debe cambiar su representación.
Requiere propagación de descanso de objetos , nombres de propiedades calculados y 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)
En su caso, todo lo que necesita hacer es encontrar el objeto en la matriz y usar el método
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>
No está mal la variante también)
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'';
Parece que la solución más simple sería usar
.map
de ES6 o _.map de
_.map
:
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;
});
Esto tiene el bonito efecto de evitar la mutación de la matriz original.
Puedes hacerlo sin usar 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")
Si el punto de inserción del nuevo objeto no necesita coincidir con el índice del objeto anterior, entonces la forma más simple de hacer esto con lodash es usar
_.reject
y luego introducir nuevos valores en la matriz:
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 tiene varios valores que desea reemplazar en una sola pasada, puede hacer lo siguiente (escrito en formato que no sea 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" }]
Si está buscando una manera de cambiar de manera inmutable la colección (como estaba cuando encontré su pregunta), puede echar un vistazo a immutability-helper , una biblioteca bifurcada de la utilidad React original. En su caso, lograría lo que mencionó a través de lo siguiente:
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"}]
Si solo está tratando de reemplazar una propiedad, lodash
_.find
y
_.set
deberían ser suficientes:
var arr = [{id: 1, name: "Person 1"}, {id: 2, name: "Person 2"}];
_.set(_.find(arr, {id: 1}), ''name'', ''New Person'');
También puede usar findIndex y pick para lograr el mismo resultado:
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);
}
Usando la función lodash unionWith, puede realizar una inserción simple a un objeto. La documentación indica que si hay una coincidencia, usará la primera matriz. Envuelva su objeto actualizado en [] (matriz) y colóquelo como la primera matriz de la función de unión. Simplemente especifique su lógica de coincidencia y, si la encuentra, la reemplazará y, de lo contrario, la agregará
Ejemplo:
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)
[ES6] Este código funciona para mí.
let result = array.map(item => item.id === updatedItem.id ? updatedItem : item)
var arr= [{id: 1, name: "Person 1"}, {id:2, name:"Person 2"}];
var index = _.findIndex(arr, {id: 1});
arr[index] = {id: 100, name: ''xyz''}
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);
}
Ahora probemos el rendimiento para todos los métodos:
// 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>