ImmutableJS ile List içindeki öğe nasıl güncellenir?


129

İşte resmi belgelerin söylediği

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>

Normal web geliştiricisinin (işlevsel programcı değil) bunu anlamasına imkan yok!

Oldukça basit (işlevsel olmayan yaklaşım için) durumum var.

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

Üçüncülist ada sahip öğenin sayısının 4 olarak ayarlandığı yeri nasıl güncelleyebilirim ?


43
Nasıl göründüğünü bilmiyorum ama dokümantasyon berbat facebook.github.io/immutable-js/docs/#/List/update
Vitalii Korsakov

71
Belgelerdeki kazı için ciddi olumlu oy. Bu sözdiziminin amacı beni aptal / yetersiz hissettirmekse, kesin başarı. Ancak amaç, Immutable'ın nasıl çalıştığını anlatmaksa, iyi ...
Owen

9
Kabul etmeliyim, immutable.js belgelerini ciddi şekilde sinir bozucu buluyorum. Bunun için kabul edilen çözüm, belgelerde hiç görmediğim findIndex () yöntemini kullanıyor.
RichardForrester

3
Bunların yerine her fonksiyon için bir örnek vermelerini tercih ederim.
RedGiant

4
Kabul. Dokümantasyon, bazı basit örnekler göstermek isteyen biri tarafından değil, bir balon içinde bazı bilim adamları tarafından yazılmış olmalı. Belgelerin bazı belgelere ihtiyacı vardır. Örneğin, alt çizgi belgelerini seviyorum, pratik örnekleri görmek çok daha kolay.
ReduxDJ

Yanıtlar:


119

En uygun durum hem kullanmaktır findIndexve updateyöntemler.

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

PS Haritaları kullanmak her zaman mümkün değildir. Örneğin, isimler benzersiz değilse ve tüm öğeleri aynı adlarla güncellemek istiyorum.


1
Yinelenen adlara ihtiyacınız varsa, çoklu haritaları veya değer olarak başlıklar içeren haritaları kullanın.
Bergi

5
Adında "üç" olan bir öğe yoksa, bu yanlışlıkla listenin son öğesini güncellemeyecek mi? Bu durumda findIndex () -1 döndürür ve bu update (), son öğenin dizini olarak yorumlanır.
Sam Storie

1
Hayır, -1 son unsur değil
Vitalii Korsakov

9
@Sam Storie haklı. Dokümanlardan: "dizin, Listenin sonundan geriye doğru indeksleyen negatif bir sayı olabilir. V.update (-1) Listedeki son öğeyi günceller." facebook.github.io/immutable-js/docs/#/List/update
Gabriel Ferraz

1
İtem.set'i çağıramadım çünkü benim için item Immutable tip değildi, önce öğeyi eşlemek, set'i çağırmak ve sonra tekrar nesneye dönüştürmek zorunda kaldım. Dolayısıyla bu örnek için function (item) {return Map (öğe) .set ("count", 4) .toObject (); }
syclee

36

.SetIn () ile aynısını yapabilirsiniz:

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

Girişin dizinini bilmiyorsak güncellemek istiyoruz. .FindIndex () kullanarak bulmak oldukça kolaydır :

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

Umarım yardımcı olur!


1
Kaçırdığınız { }içeride fromJS( )geçerli JS değil ayraçlar olmadan.
Andy

1
Döndürülen nesneye bir nesne olduğu için 'arr' demezdim. sadece arr.elem bir dizidir
Nir O.

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

açıklama

Immutable.js koleksiyonlarının güncellenmesi, her zaman orijinali değiştirmeden bırakarak bu koleksiyonların yeni sürümlerini döndürür. Bu nedenle JavaScript'in list[2].count = 4mutasyon sözdizimini kullanamıyoruz . Bunun yerine, Java toplama sınıflarında yapabileceğimiz gibi, yöntemleri çağırmamız gerekir.

Daha basit bir örnekle başlayalım: sadece bir listedeki sayılar.

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

Biz 3 maddeyi güncelleştirmek isteseydim, düz JS dizi gibi görünebilir: counts[2] = 4. Mutasyonu kullanamadığımızdan ve bir yöntem çağırmamız gerektiğinden, bunun yerine şunu kullanabiliriz: counts.set(2, 4)- bu 4, dizindeki değeri ayarlamak anlamına gelir 2.

Derin güncellemeler

Verdiğiniz örnekte iç içe geçmiş veriler var. set()İlk koleksiyonda kullanamayız .

Immutable.js koleksiyonları, iç içe geçmiş bir kümede daha derin değişiklikler yapmanıza olanak tanıyan "In" ile biten adlara sahip bir yöntem ailesine sahiptir. En yaygın güncelleme yöntemlerinin ilgili bir "Giriş" yöntemi vardır. Örneğin setvar setIn. Bir indeksi veya anahtarı ilk argüman olarak kabul etmek yerine, bu "Giriş" yöntemleri bir "anahtar yolu" kabul eder. Anahtar yolu, güncellemek istediğiniz değere nasıl ulaşılacağını gösteren bir dizi dizin veya anahtar dizisidir.

Örneğinizde, 2. dizindeki listedeki öğeyi ve ardından bu öğe içindeki "say" anahtarındaki değeri güncellemek istediniz. Yani anahtar yol olacaktır [2, "count"]. setInYöntemin ikinci parametresi tıpkı setoraya koymak istediğimiz yeni değer gibi çalışır , yani:

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

Doğru anahtar yolunu bulmak

Bir adım daha ileri giderek , adı "üç" olan öğeyi güncellemek istediğinizi söylediniz , bu yalnızca 3. öğeden farklıdır. Örneğin, listeniz sıralanmamış olabilir veya belki de "iki" adlı öğe daha önce kaldırılmıştır. Bu, öncelikle doğru anahtar yolunu bildiğimizden emin olmamız gerektiği anlamına gelir! Bunun için findIndex()yöntemi kullanabiliriz (bu arada, neredeyse tam olarak Array # findIndex gibi çalışır ).

Listede güncellemek istediğimiz öğenin bulunduğu dizini bulduğumuzda, güncellemek istediğimiz değere giden anahtar yolu sağlayabiliriz:

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

NB: SetvsUpdate

Orijinal soru, ayarlanan yöntemlerden çok güncelleme yöntemlerinden bahseder. Bu işlevdeki ikinci argümanı (çağrılan updater) açıklayacağım , çünkü ondan farklı set(). İkinci argüman set()istediğimiz yeni değer iken, ikinci argüman önceki değeri kabul eden ve istediğimiz yeni değeri döndüren update()bir fonksiyondur . Daha sonra, anahtar yolu kabul eden updateIn()"Giriş" varyasyonudur update().

Örneğin, örneğinizin yalnızca sayıyı ayarlamakla kalmayan 4, bunun yerine mevcut sayımı artıran bir varyasyonunu istediğimizi varsayalım, mevcut değere bir ekleyen bir işlev sağlayabiliriz:

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

19

İşte resmi belgelerin söylediği ... updateIn

İhtiyacınız yok updateIn, bu sadece iç içe geçmiş yapılar içindir. Çok daha basit bir imzası ve dokümantasyonu olan updateyöntemi arıyorsunuz :

Güncelleyiciyi mevcut değerle çağırmanın dönüş değeri ile dizinde güncellenmiş bir değere sahip yeni bir Liste veya dizin ayarlanmamışsa notSetValue döndürür.

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

bu, Map::updatedokümanların önerdiği gibi " şuna eşdeğerdir:list.set(index, updater(list.get(index, notSetValue))) ".

"üçüncü" adlı öğe

Listeler böyle çalışmaz. Güncellemek istediğiniz öğenin dizinini bilmeniz veya aramanız gerekir.

Üçüncü ada sahip öğenin sayısının 4 olarak ayarlandığı listeyi nasıl güncelleyebilirim?

Bunu yapmalı:

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

14
Hayır, veri yapısı seçiminiz iş mantığını karşılamıyor :-) Öğelere adlarına göre erişmek istiyorsanız, liste yerine bir harita kullanmalısınız.
Bergi

1
@bergi, gerçekten mi? Öyleyse, diyelim ki bir sınıftaki öğrencilerin bir listesi varsa ve bu öğrenci verileri üzerinde CRUD işlemleri yapmak istersem, a Mapover a kullanarak Listönereceğiniz yaklaşım nedir? Yoksa sadece OP'nin "öğeyi adıyla seç" dediği ve "öğeyi kimliğe göre seç" demesi nedeniyle mi söylüyorsunuz?
David Gilbertson

2
@DavidGilbertson: CRUD? Bir veritabanı öneririm :-) Ama evet, bir id-> öğrenci haritası, üzerinde bir siparişiniz yoksa ve dizine göre seçmek istemediğiniz sürece, bir listeden daha uygun görünüyor.
Bergi

1
@bergi Sanırım aynı fikirde olmayacağımızı kabul edeceğiz, API'lerden kimliklere sahip şeyler dizileri biçiminde, bunları kimliğe göre güncellemem gereken bol miktarda veri alıyorum. Bu bir JS Dizisidir ve aklımda bu nedenle immutable JS listesi olmalıdır.
David Gilbertson

1
@DavidGilbertson: Bu API'lerin açıkça sıralanmamış olan setler için JSON dizilerini aldığını düşünüyorum.
Bergi

12

.Map () kullanın

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
Bunun için teşekkürler - burada çok sayıda korkunç karmaşık cevap var, gerçek cevap "bir listeyi değiştirmek istiyorsanız, haritayı kullanın". Kalıcı değişmez veri yapılarının tüm noktası budur, onları "güncellemiyorsunuz", içindeki güncellenmiş değerlerle yeni bir yapı oluşturuyorsunuz; ve bunu yapmak ucuzdur çünkü değiştirilmemiş liste öğeleri paylaşılır.
Korny

2

Thomastuts web sitesindeki bu yaklaşımı gerçekten beğendim :

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

Şunları kullanabilirsiniz map:

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

Ancak bu, tüm koleksiyonda yinelenecek.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.