일치 항목을 대체하는 기능이 lodash에 있습니까?


134

lodash에 JavaScript 컬렉션의 항목을 대체하는 더 간단한 방법이 있는지 궁금합니다. (가능한 중복 이지만 거기에 대한 답변을 이해하지 못했습니다 :)

나는 그들의 문서를 보았지만 아무것도 찾을 수 없었다

내 코드는 다음과 같습니다

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

업데이트 : 교체하려는 객체에는 다음과 같은 많은 속성이 있습니다.

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

해결책:

dfsq 덕분에 lodash 내에서 제대로 작동하는 것처럼 보이고 꽤 깔끔한 적절한 솔루션을 찾았 으며이 요구 사항을 여러 곳에서 보냈으므로 믹스 인에 넣었습니다. 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));

더 빠른 솔루션 @dfsq에서 지적했듯이 다음은 더 빠릅니다.

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

7
"Faster Solution"의 liine 4에서 _.indexOf의 두 번째 매개 변수로 match를 사용할 수 있다고 생각합니다. 거기에서 해당 값을 다시 계산할 필요가 없으므로 조금 더 빠릅니다.
davertron

2
더 빠르다 : _.findIndex일치를 위해 사용하십시오 .
Julian K

1
@JulianK와 @davertron이 말한 것을 확장하기 위해 _.findIndex대신에 _.find를 사용 하면 두 번째 _.find와를 모두 삭제할 수 있습니다 _.indexOf. 필요한 것은 1 일 때 배열을 3 번 반복하는 것입니다.
Justin Morgan

답변:


191

귀하의 경우 배열에서 객체를 찾고 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>


1
글쎄, 귀하의 솔루션은 성능면에서 비용이 많이들 indexOf것입니다. 왜냐하면 매우 빠를 것입니다 (기본 브라우저 Array.prototype.indexOf를 사용합니다). 그러나 어쨌든, 당신에게 맞는 솔루션을 찾으십시오.
dfsq

14
왜 사용하지 _.findIndex않습니까? 그런 다음을 사용할 필요가 없습니다 _.indexOf.
AJ Richardson

35

가장 간단한 솔루션은 ES6 .map또는 lodash 를 사용하는 것처럼 보입니다 _.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;
});

이것은 원래 배열을 변경하지 않는 좋은 효과가 있습니다.


8
하지만 매번 새로운 배열을 생성하고 있습니다.
kboom

3
그러나 새 배열을 만들지 않는 유일한 대안은 기존 배열을 변경하는 것입니다. 또한 새 어레이를 생성해도 성능 측면에는 영향을 미치지 않습니다. 저에게서 공감하십시오.
노비타

24

[ES6] 이 코드는 저에게 효과적입니다.

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

1. 새로운 배열 인스턴스를 생성하고 있으므로 항목을 "대체"하는 것은 아닙니다. 2. updatedItemif 배열에 같은 항목이 포함되어 있지 않으면 유실됩니다 id.
악의적 인

이것은 '업데이트'가 아닌 '업데이트'에 대한 해결책입니다 (질문은 "일치하는 항목을 대체하는 lodash에 함수가 있습니까?"). 그렇습니다. 배열의 사본을 생성하므로 같은 배열 (내가하지 않았다)
shebik

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

이제 모든 방법에 대한 성능을 테스트 해 보겠습니다.


6
사람들에게 부정적인 영향을 미치기 때문에 "공격을 당할 수 있습니다." 내가 "현명한 사람이기 때문에 당신이 당신의 감정에 대해 게으르지 않고 그것에 대해 생각할 것"이라고 말하면서 이것을 완성했다고 상상해보십시오.
Aditya MP

5
나는 누군가를 다치게하고 싶지 않았지만, 원작자 제작자의 접근 방식보다 최악의 솔루션이 어떻게 많은 투표를 받았는지 궁금합니다. 그것에 투표를 한 사람들은 어떤 규칙을 적용합니까? 그리고 나는 사람들이 가장 많이 투표 한 답변을 맹목적으로 믿으며 비판적 사고가 없다고 불만을 나타 냈습니다.
사악한

1
@evilive 유효한 포인트이지만, 이전에 답변 / 투표를 제공 한 모든 사람이 바보 인 것처럼 당신이 어떻게 당신을 만나야하는지 모르겠습니다. 이 답변의 사실 부분은 훌륭하고 나머지는 간신히 포함 된 우월 단지의 공기를 가지고 있습니다. 그것은 누구에게도 도움이되지 않습니다. 과도한 감정적 반응없이 쉽게 요점을 지적 할 수 있습니다.
Thor84no

1
솔루션과 TC의 솔루션은 ID로만 필터링된다는 점에 유의해야합니다. 이것이 그 두 가지가 더 빨리 달리는 첫 번째 이유입니다. 다른 두 개는 필터링에 필요한 객체의 어떤 부분이든 전달할 수있게하는데 upsert 함수로 더 선호 될 수 있습니다.
Aram

10

findIndex 및 pick을 사용하여 동일한 결과를 얻을 수도 있습니다.

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

6

시간이 지남에 따라 데이터 변이를 피하고 작은 단일 책임 기능을 작성하는보다 기능적인 접근 방식을 수용해야합니다. ECMA 스크립트 (6) 표준을 사용하면 제공과 자바 스크립트에서 함수형 프로그래밍 패러다임을 즐길 수있는 map, filterreduce방법. 다른 기본, 밑줄 또는 가장 기본적인 작업을 수행 할 다른 작업이 필요하지 않습니다.

아래에는 다른 언어 기능을 사용하여이 문제를 해결하는 방법을 보여주기 위해이 문제에 대한 제안 된 솔루션이 포함되어 있습니다.

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)


재귀 버전-매핑과 동일합니다.

파괴배열 확산이 필요합니다 .

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)


최종 배열의 순서가 중요하지 않은 object경우 HashMap 데이터 구조 로 사용할 수 있습니다 . 이미 키 컬렉션을 키로 사용하는 경우 매우 편리합니다. object그렇지 않으면 먼저 표현을 변경해야합니다.

객체 레스트 스프레드 , 계산 된 속성 이름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)


5

당신은 단지 하나의 속성, lodash를 교체하려는 경우 _.find_.set충분해야한다 :

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

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

1

새 객체의 삽입 점이 이전 객체의 색인과 일치하지 않아도되는 경우 lodash를 사용하여이를 수행하는 가장 간단한 방법은 _.reject새 값을 사용하여 배열에 넣는 것입니다.

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" }]

한 번에 여러 값을 바꾸려는 경우 다음을 수행 할 수 있습니다 (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" }]

이 방법은 배열 정렬을 변경합니다
sospedra

1

lodash union 사용 함수를 사용하면 객체에 대한 간단한 upsert를 수행 할 수 있습니다. 설명서에는 일치하는 항목이 있으면 첫 번째 배열이 사용된다고 명시되어 있습니다. 업데이트 된 객체를 [] (배열)로 감싸서 공용 함수의 첫 번째 배열로 둡니다. 일치하는 논리를 지정하고 발견되면이를 대체하고 추가하지 않는 경우 논리를 추가하십시오.

예:

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)

1

나쁜 변형도 아닙니다)

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

1
var arr= [{id: 1, name: "Person 1"}, {id:2, name:"Person 2"}];
var index = _.findIndex(arr, {id: 1});
arr[index] = {id: 100, name: 'xyz'}

0

컬렉션을 불변으로 변경하는 방법을 찾고 있다면 (질문을 찾은 것처럼) 원래 React 유틸리티에서 가져온 라이브러리 인 불변성 도우미를 살펴볼 수 있습니다 . 귀하의 경우 다음을 통해 언급 한 것을 달성 할 수 있습니다.

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

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") 

0

불변 , 적합 ReactJS:

취하다:

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

업데이트 된 항목이 두 번째이고 이름이 다음으로 변경됩니다 Special Person.

const updatedItem = {id:2, name:"Special Person"};

힌트 : lodash 에는 유용한 도구가 있지만 이제는 Ecmascript6 +에 일부 도구가 있으므로및map에 존재하는함수를사용합니다.lodashecmascript6+

const newArr = arr.map(item => item.id === 2 ? updatedItem : item);

0

이뿐 만 아니라 그것을 가로 질러 간단하게 그렇게했습니다.

const persons = [{id: 1, name: "Person 1"}, {id:2, name:"Person 2"}];
const updatedPerson = {id: 1, name: "new Person Name"}
const updatedPersons = persons.map(person => (
  person.id === updated.id
    ? updatedPerson
    : person
))

원한다면 일반화 할 수 있습니다

const replaceWhere = (list, predicate, replacement) => {
  return list.map(item => predicate(item) ? replacement : item)
}

replaceWhere(persons, person => person.id === updatedPerson.id, updatedPerson)
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.