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


129

これは公式ドキュメントが言ったことです

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

list名前が3番目の要素のカウント4に設定されている場所を更新するにはどうすればよいですか?


43
どのように見えるかはわかりませんが、ドキュメントはひどいですfacebook.github.io/immutable-js/docs/#/List/update
Vitalii Korsakov

71
ドキュメントで発掘のための深刻な賛成票。その構文の目標が私を愚か/不十分、明確な成功に感じさせることである場合。ただし、目標が不変のしくみを伝えることである場合は...
Owen

9
私も同意する必要があります。immutable.jsのドキュメントは非常にイライラするものです。これに対する受け入れられた解決策は、ドキュメントにまったく表示されていないfindIndex()メソッドを使用します。
RichardForrester 2016

3
それらではなく、各機能の例を示します。
RedGiant

4
同意した。ドキュメントは、いくつかの科学者によって、簡単な例を示したいと思っている人ではなく、バブルで書かれている必要があります。ドキュメントにはいくつかのドキュメントが必要です。たとえば、私はアンダースコアのドキュメントが好きで、実用的な例がはるかに見やすくなっています。
ReduxDJ

回答:


119

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

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

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


1
重複する名前が必要な場合は、マルチマップ、または値としてタプルを使用したマップを使用します。
Bergi、

5
名前が「3」の要素がない場合、リストの最後の要素を誤って更新しませんか?その場合、findIndex()は-1を返し、update()は最後の要素のインデックスとして解釈します。
Sam Storie

1
いいえ、-1は最後の要素ではありません
Vitalii Korsakov

9
@Sam Storieは正しいです。ドキュメントから:「インデックスは負の数である場合があり、リストの最後からインデックスを付けます。v.update(-1)はリストの最後のアイテムを更新します。」 facebook.github.io/immutable-js/docs/#/List/update
Gabriel Ferraz

1
私の場合、アイテムは不変タイプではなかったため、item.setを呼び出すことができませんでした。最初にアイテムをマップし、setを呼び出してから、もう一度オブジェクトに変換する必要がありました。したがって、この例では、function(item){return Map(item).set( "count"、4).toObject(); }
2016

36

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

それが役に立てば幸い!


1
{ }中が足りfromJS( )ないので、中括弧がないと有効なJSではありません。
アンディ

1
返されたオブジェクトはオブジェクトであるため、「arr」とは呼びません。配列はarr.elemのみです
Nir O.

21
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番目のアイテムだけではないアイテムを更新したいと実際に言った。たとえば、リストが並べ替えられていないか、「2」という名前のアイテムが以前に削除された可能性がありますか?つまり、最初に正しいキーパスを実際に知っていることを確認する必要があります。これには、findIndex()メソッドを使用できます(ちなみに、これはArray#findIndexとほぼ同じように機能します)。

更新するアイテムを含むリストでインデックスを見つけたら、更新する値へのキーパスを指定できます。

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

注意:SetvsUpdate

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

たとえば、カウントを4に設定するだけでなく、既存のカウントをインクリメントするバリエーションの例が必要だとすると、既存の値に1を追加する関数を提供できます。

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

19

これが公式のドキュメントが言ったことです… updateIn

は必要ありませんupdateIn。これはネストされた構造にのみ使用されます。より単純なシグネチャとドキュメントを持つupdatemethodを探しています。

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

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

これは、Map::updateドキュメントが示唆しているように、「と同等list.set(index, updater(list.get(index, notSetValue)))」です。

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

それはリストが機能する方法ではありません。更新する要素のインデックスを知っているか、それを検索する必要があります。

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

これはそれを行うはずです:

list = list.update(2, function(v) {
    return {id: v.id, name: v.name, count: 4};
});

14
いいえ、選択したデータ構造はビジネスロジックを満たしていません:-)名前で要素にアクセスする場合は、リストではなくマップを使用する必要があります。
ベルギ

1
@bergi、本当に?たとえば、クラスの生徒のリストがあり、この生徒のデータに対してCRUD操作を実行したい場合は、Mapover a を使用するList方法をお勧めしますか?それとも、OPが「IDでアイテムを選択」ではなく「名前でアイテムを選択」と言ったので、それだけを言っていますか?
David Gilbertson、2015年

2
@DavidGilbertson:CRUD?データベースをお勧めします:-)しかし、はい、id-> student mapはリストよりも適切に聞こえます。
Bergi、2015年

1
@bergi私は同意しないだろうと思います。IDを持つものの配列の形でAPIから大量のデータを取得します。IDでこれらのものを更新する必要があります。これはJS配列であり、私の考えでは、不変のJSリストにする必要があります。
David Gilbertson、2015年

1
@DavidGilbertson:これらのAPIはセットにJSON配列をとっていると思います-これは明示的に順序付けされていません。
Bergi、2015年

12

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


1
それをありがとう-ここで非常に多くのひどい複雑な答え、本当の答えは「リストを変更したい場合はマップを使う」です。これが永続的な不変データ構造の要点です。データ構造を「更新」するのではなく、更新された値で新しい構造を作成します。変更されていないリスト要素が共有されるため、そうすることは安価です。
Korny、2017年

2

私はトーマストのウェブサイトからのこのアプローチが本当に好きです:

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

-2

使用できますmap

list = list.map((item) => { 
    return item.get("name") === "third" ? item.set("count", 4) : item; 
});

しかし、これはコレクション全体に対して繰り返されます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.