web-dev-qa-db-ja.com

ImmutableJSでリスト内の要素を更新する方法は?

公式ドキュメントは次のとおりです

updateIn(keyPath: Array<any>, updater: (value: any) => any): List<T>
updateIn(keyPath: Array<any>, notSetValue: any, updater: (value: any) => any): List<T>
updateIn(keyPath: Iterable<any, any>, updater: (value: any) => any): List<T>
updateIn(keyPath: Iterable<any, any>, notSetValue: any, updater: (value: any) => any): List<T>

(プログラマーではなく)通常のWeb開発者がそれを理解する方法はありません!

私は非常にシンプルな(非機能的なアプローチのための)ケースを持っています。

var arr = [];
arr.Push({id: 1, name: "first", count: 2});
arr.Push({id: 2, name: "second", count: 1});
arr.Push({id: 3, name: "third", count: 2});
arr.Push({id: 4, name: "fourth", count: 1});
var list = Immutable.List.of(arr);

名前がthirdの要素がcount4に設定しているlistを更新するにはどうすればよいですか?

122

最も適切なケースは、findIndexメソッドとupdateメソッドの両方を使用することです。

list = list.update(
  list.findIndex(function(item) { 
    return item.get("name") === "third"; 
  }), function(item) {
    return item.set("count", 4);
  }
); 

追伸マップを常に使用できるとは限りません。例えば。名前が一意ではなく、すべてのアイテムを同じ名前で更新したい場合。

114

。setIn() を使用すると、同じことができます。

let obj = fromJS({
  elem: [
    {id: 1, name: "first", count: 2},
    {id: 2, name: "second", count: 1},
    {id: 3, name: "third", count: 2},
    {id: 4, name: "fourth", count: 1}
  ]
});

obj = obj.setIn(['elem', 3, 'count'], 4);

更新するエントリのインデックスがわからない場合。 。findIndex() を使用して見つけるのは非常に簡単です:

const indexOfListToUpdate = obj.get('elem').findIndex(listItem => {
  return listItem.get('name') === 'third';
});
obj = obj.setIn(['elem', indexOfListingToUpdate, 'count'], 4);

それが役に立てば幸い!

27
Albert Olivé

公式ドキュメントの説明は…updateIn

updateInは必要ありません。これは、ネストされた構造専用です。 update method を探しています。これには、はるかに簡単な署名とドキュメントがあります。

既存の値でupdaterを呼び出したときの戻り値を使用して、インデックスで更新された値を使用して新しいリストを返します。インデックスが設定されていない場合はnotSetValueを返します。

update(index: number, updater: (value: T) => T): List<T>
update(index: number, notSetValue: T, updater: (value: T) => T): List<T>

Map::update docs が示唆するように、「と同等:list.set(index, updater(list.get(index, notSetValue)))」.

「サード」という名前の要素

それがリストの仕組みではありません。更新する要素のインデックスを知るか、検索する必要があります。

Thirdという名前の要素のカウントが4に設定されているリストを更新するにはどうすればよいですか?

これはそれを行う必要があります:

list = list.update(2, function(v) {
    return {id: v.id, name: v.name, count: 4};
});
18
Bergi
var index = list.findIndex(item => item.name === "three")
list = list.setIn([index, "count"], 4)

説明

Immutable.jsコレクションを更新すると、常にそれらのコレクションの新しいバージョンが返され、元のコレクションは変更されません。そのため、JavaScriptのlist[2].count = 4突然変異構文を使用することはできません。代わりに、Javaコレクションクラスで行うのと同じように、メソッドを呼び出す必要があります。

より単純な例から始めましょう。リスト内のカウントだけです。

var arr = [];
arr.Push(2);
arr.Push(1);
arr.Push(2);
arr.Push(1);
var counts = Immutable.List.of(arr);

3番目の項目を更新する場合、プレーンなJS配列はcounts[2] = 4のようになります。突然変異を使用することはできず、メソッドを呼び出す必要があるため、代わりにcounts.set(2, 4)を使用できます。つまり、インデックス4に値2を設定します。

ディープアップデート

ただし、指定した例にはネストされたデータがあります。最初のコレクションでset()を使用することはできません。

Immutable.jsコレクションには、名前が「In」で終わるメソッドファミリがあり、ネストされたセットでより深い変更を行うことができます。最も一般的な更新方法には、関連する「In」メソッドがあります。たとえば、setにはsetInがあります。最初の引数としてインデックスまたはキーを受け入れる代わりに、これらの「In」メソッドは「キーパス」を受け入れます。キーパスは、更新する値に到達する方法を示すインデックスまたはキーの配列です。

この例では、インデックス2のリスト内のアイテムを更新してから、そのアイテム内のキー「count」の値を更新します。したがって、キーパスは[2, "count"]になります。 setInメソッドの2番目のパラメーターはsetと同じように機能します。これは、そこに配置する新しい値です。

list = list.setIn([2, "count"], 4)

正しいキーパスを見つける

さらに一歩進めて、実際には、名前が「3」であるアイテムを更新したいと言いましたが、これは3番目のアイテムとは異なります。たとえば、リストが並べ替えられていないか、「two」という名前のアイテムが以前に削除されていませんか?つまり、最初に正しいキーパスを実際に知っていることを確認する必要があります。このためにfindIndex()メソッドを使用できます(ちなみに、これは Array#findIndex とほぼ同じように機能します)。

リスト内で更新したい項目を持つインデックスを見つけたら、更新したい値へのキーパスを提供できます:

var index = list.findIndex(item => item.name === "three")
list = list.setIn([index, "count"], 4)

NB:Set vs Update

元の質問では、setメソッドではなくupdateメソッドに言及しています。 set()とは異なるため、この関数の2番目の引数(updaterと呼ばれる)について説明します。 set()の2番目の引数は必要な新しい値ですが、update()の2番目の引数はfunctionで、以前の値を受け入れて必要な新しい値を返します。次に、updateIn()は、update()の「In」バリエーションであり、キーパスを受け入れます。

たとえば、カウントを4に設定するだけでなく、既存のカウントをincrementedする代わりに、既存の値:

var index = list.findIndex(item => item.name === "three")
list = list.updateIn([index, "count"], value => value + 1)
16
Lee Byron

。map() を使用します

list = list.map(item => 
   item.get("name") === "third" ? item.set("count", 4) : item
);
var arr = [];
arr.Push({id: 1, name: "first", count: 2});
arr.Push({id: 2, name: "second", count: 1});
arr.Push({id: 3, name: "third", count: 2});
arr.Push({id: 4, name: "fourth", count: 1});
var list = Immutable.fromJS(arr);

var newList = list.map(function(item) {
    if(item.get("name") === "third") {
      return item.set("count", 4);
    } else {
      return item;
    }
});

console.log('newList', newList.toJS());

// More succinctly, using ES2015:
var newList2 = list.map(item => 
    item.get("name") === "third" ? item.set("count", 4) : item
);

console.log('newList2', newList2.toJS());
<script src="https://cdnjs.cloudflare.com/ajax/libs/immutable/3.8.1/immutable.js"></script>
9
Meistro

thomastuts ウェブサイトのこのアプローチが本当に好きです:

const book = fromJS({
  title: 'Harry Potter & The Goblet of Fire',
  isbn: '0439139600',
  series: 'Harry Potter',
  author: {
    firstName: 'J.K.',
    lastName: 'Rowling'
  },
  genres: [
    'Crime',
    'Fiction',
    'Adventure',
  ],
  storeListings: [
    {storeId: 'Amazon', price: 7.95},
    {storeId: 'barnesnoble', price: 7.95},
    {storeId: 'biblio', price: 4.99},
    {storeId: 'bookdepository', price: 11.88},
  ]
});

const indexOfListingToUpdate = book.get('storeListings').findIndex(listing => {
  return listing.get('storeId') === 'Amazon';
});

const updatedBookState = book.setIn(['storeListings', indexOfListingToUpdate, 'price'], 6.80);

return state.set('book', updatedBookState);
0
Dryden Williams