一致したアイテムを置き換えるための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の2番目のパラメーターとしてmatchを使用することもできると思います。そこで値を再計算する必要がないため、処理が少し速くなります。
davertron

2
さらに高速:_.findIndex一致に使用します。
ジュリアンK

1
@JulianKと@davertronが言ったことを拡張するために、_.findIndex代わりに_.findを使用すると、2番目_.findとの両方をドロップできます_.indexOf。あなたが必要とするすべてが1のときには、配列を3回反復している
ジャスティン・モーガン

回答:


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 2014

14
なぜ使用しないの_.findIndexですか?その後、を使用する必要はありません_.indexOf
AJリチャードソン

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 2017

3
ただし、新しい配列を作成しない唯一の方法は、既存の配列を変更することです。また、新しいアレイを作成しても、パフォーマンスの点で影響はほとんどありません。私から賛成票を投じます。
のび太

24

[ES6]このコードは私にとってはうまくいきます。

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

1.配列の新しいインスタンスを作成しているので、アイテムを実際に「置き換える」ことはできません。2. updatedItem配列に同じの項目が含まれていない場合は、失われますid
evilive

これは「upsert」ではなく「update」の解決策です(質問は「MATCHEDアイテムを置き換えるための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 2017年

5
誰も傷つけたくはありませんでしたが、元のトピック作成者のアプローチよりも最悪の解決策がどうして多くの票を獲得したのでしょうか。そのために投票した人々は何を支配しますか?そして、私は人々が最も投票された答えを盲目的に信頼し、批判的な考えを持っていないことに失望しました。
evilive

1
@evilive有効なポイントですが、以前に回答/投票を提供したすべての人が馬鹿であるかのように、それらに遭遇するように要求する方法はわかりません。この答えの事実の部分は素晴らしいです、残りはかろうじて含まれている優越複合体の空気を持っています。それは誰の助けにもなりません。あなたは過度の感情的な反応なしにあなたのポイントを簡単に作ることができます。
Thor84no

1
ただし、ソリューションとTCのソリューションはIDでのみフィルタリングしていることに注意してください。これが、これら2つがより速く実行される最初の理由です。他の2つでは、フィルタリングに必要なオブジェクトの任意の部分を渡すことができます。これは、upsert関数としてより適している場合があります。
アラム2017

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

時間が経過するにつれて、データの変更を回避し、小さな単一責任関数を作成する、より機能的なアプローチを採用する必要があります。ECMAScriptの6標準を使用すると、提供してJavaScriptで関数型プログラミングのパラダイムを楽しむことができるmapfilterreduce方法。別のロダッシュ、アンダースコア、または最も基本的なことを実行するために他に何も必要ありません。

以下に、さまざまな言語機能を使用してこの問題を解決する方法を示すために、この問題に対するいくつかの提案された解決策を示します。

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

あなただけの1プロパティ、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" }]

1つのパスで置き換える複数の値がある場合は、次の操作を実行できます(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 '22

1

lodash unionWith関数を使用すると、オブジェクトへの単純なアップサートを実行できます。ドキュメントには、一致がある場合、最初の配列が使用されると記載されています。更新したオブジェクトを[](配列)でラップし、それを共用体関数の最初の配列として配置します。単に一致するロジックを指定し、見つかった場合はそれを置き換え、見つからない場合は追加します

例:

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


0

コレクションを不変に変更する方法を探しているなら(私があなたの質問を見つけたときのように)、元のReact utilから分岐したライブラリーであるimmutability-helperを調べることができます。あなたのケースでは、あなたはあなたがあなたが述べたことを以下を介して成し遂げるでしょう:

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

更新されたアイテムは2番目で、名前は次のように変更されSpecial Personます。

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

ヒント lodashには便利なツールがありますが、Ecmascript6 +にいくつかあるためmaplodashおよびの両方に存在する関数を使用しますecmascript6+

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.