codememo

lodash에 일치하는 아이템을 대체할 수 있는 기능이 있습니까?

tipmemo 2023. 10. 20. 13:41
반응형

lodash에 일치하는 아이템을 대체할 수 있는 기능이 있습니까?

lodash에서 자바스크립트 컬렉션의 항목을 대체할 수 있는 더 간단한 방법이 있는지 궁금합니다. (중복 가능하지만 답을 이해하지 못했습니다.)

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

내 코드는:

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에서 적절한 해결책을 찾았고 여러 곳에서 요구사항이 있어서 믹신에도 넣었습니다.제이에스빈

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

당신의 경우에는 배열된 개체를 찾아서 사용하기만 하면 됩니다.Array.prototype.splice()method, 자세한 내용은 여기를 참조하십시오.

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>

ES6를 사용하는 것이 가장 간단한 해결책인 것 같습니다..map아니면 로다시의_.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;
});

이렇게 하면 원래 배열이 변형되는 것을 피할 수 있습니다.

[ES6] 이 코드는 저에게 적합합니다.

let result = array.map(item => item.id === updatedItem.id ? updatedItem : item)
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);
}

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

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

단지 하나의 부동산을 교체하려는거라면, 로대시_.find그리고._.set충분해야 합니다:

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

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

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

시간이 지남에 따라 데이터의 변동을 방지하고 소규모 단일 책임 기능을 작성해야 하는 보다 기능적인 접근 방식을 채택해야 합니다.ECMAscript 6 표준을 사용하면, 당신은 제공된 것과 함께 자바스크립트로 기능적 프로그래밍 패러다임을 즐길 수 있습니다.map,filter그리고.reduce방법들. 기본적인 일들을 하기 위해 또 다른 로대시나 언더스코어, 또는 그 밖에 무엇을 할 필요는 없습니다대부분의 기본적인 일들을 하기 위해 또 다른 로대시, 밑줄 또는 그 밖에 무엇을 할 필요가 없습니다.

아래에는 이 문제를 다른 언어 기능을 사용하여 해결할 수 있는 방법을 보여주기 위해 이 문제에 대해 제안된 몇 가지 해결책이 포함되어 있습니다.

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)


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

개체 휴식 스프레드, 계산된 속성 이름Object.entry 필요합니다.

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)

이것도 우연히 발견했고 간단하게 그렇게 했습니다.

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)

lodash union 사용하기함수를 사용하면 객체에 대한 간단한 업셋을 수행할 수 있습니다.설명서에는 일치하는 항목이 있으면 첫 번째 배열을 사용한다고 나와 있습니다.업데이트된 개체를 [ ](array)에 랩핑한 후 유니온 함수의 첫 번째 배열로 넣습니다.일치하는 논리를 지정하기만 하면 해당 논리가 대체되고 그렇지 않으면 추가됩니다.

예:

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)
var arr= [{id: 1, name: "Person 1"}, {id:2, name:"Person 2"}];
var index = _.findIndex(arr, {id: 1});
arr[index] = {id: 100, name: 'xyz'}

새 개체의 삽입 지점이 이전 개체의 인덱스와 일치할 필요가 없는 경우 lodash로 이 작업을 수행하는 가장 간단한 방법은 다음을 사용한 다음 새 값을 배열에 밀어 넣는 것입니다.

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

기능을 만들어서 "lodash-ey"를 유지하고 싶다면 콜백과 함께 작동하는 포장지 기능을 만들 수 있습니다.기능을 보다 일반적으로 사용할 수 있게 해줍니다.

이 글을 쓰려면 다음과 같은 것을 시도해보세요.

function findAllAndReplace(array, replacement, callback){
    return array.map( element => callback(element) ? replacement : element )
}

키로 찾아서 바꾸려면 콜백을 아주 간단하게 하면 됩니다.(itemInArray) => itemInArray.keyOnItem

그러나 고급 기능을 원한다면 별도의 노력 없이도 기능을 통합할 수 있습니다.여기 몇 가지 예가 있어요.

  1. (간편) id 2로 아이템 찾기, id로 바꾸기: 7
const items = [{id: 1}, {id: 2}, {id: 3}, {id: 4}, {id: 5}]

findAllAndReplace( items, {id: 7}, item => item.id === 2 )
  1. (약간 더 복잡함) 28살 존을 찾아서 28살 존으로 대체합니다.

const people = [
    {
        name: "John",
        age: 20
    },
    {
        name: "John",
        age: 28
    },
    {
        name: "Jim",
        age: 28
    },
]


findAllAndReplace(
    people, // all the people
    { name: "Jon", age: 28 }, // Replacement value
    (person) => person.name === "jon" && person.age === 21 // callback function
)

또한 위의 방법은 일치하는 모든 인스턴스를 찾아서 대체하지만, 하나를 위해 이를 수행하고 싶다면 아래와 같은 작업을 수행할 수 있습니다.

function findOneAndReplace(array, replacement, callback){
    const splitIndex = array.findIndex(callback)
    
    // This if statement can be ommitted, but might 
    // be handy depending on your use case
    if(splitIndex < 0){
        throw new Error("Swap Element not found")
    }

    const leadingTerms = array.slice(0, splitIndex)
    const trailingTerms = array.slice(splitIndex + 1, array.length)
    return [...leadingTerms, replacement, ...trailingTerms]
)

참고: 일치하는 요소를 찾지 못할 경우 기능이 중단되도록 하는 것이 유용할 수 있지만, 해당 기능을 사용하지 않으려면 해당 코드 라인을 잘라낼 수 있습니다.

나쁘지 않은 변형)

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

(질문을 발견했을 때와 같이) 컬렉션을 불변으로 변경할 수 있는 방법을 찾고 있다면 원래 반응 유틸리티에서 파생된 라이브러리인 불변성 도우미를 살펴볼 수 있습니다.당신의 경우, 당신은 다음을 통해 당신이 언급한 것을 성취할 것입니다.

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

로대쉬를 사용하지 않고도 할 수 있습니다.

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

불변, 적합한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둘 다에 존재하는 함수lodash그리고.ecmascript6+:

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

언급URL : https://stackoverflow.com/questions/27641731/is-there-a-function-in-lodash-to-replace-matched-item

반응형