Oto, co powiedzieli oficjalni doktorzy
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>
Nie ma mowy, żeby zwykły twórca stron internetowych (nie funkcjonalny programista) to zrozumiał!
Mam dość prosty (dla niefunkcjonalnego podejścia) przypadek.
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);
Jak mogę zaktualizować, list
gdzie element o nazwie trzeciej ma liczbę ustawioną na 4 ?
javascript
functional-programming
immutable.js
Vitalii Korsakov
źródło
źródło
Odpowiedzi:
Najbardziej odpowiednim przypadkiem jest użycie obu metod
findIndex
iupdate
.list = list.update( list.findIndex(function(item) { return item.get("name") === "third"; }), function(item) { return item.set("count", 4); } );
PS Nie zawsze można korzystać z Map. Np. Jeśli nazwy nie są unikalne i chcę zaktualizować wszystkie elementy o tych samych nazwach.
źródło
Z .setIn () możesz zrobić to samo:
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);
Jeśli nie znamy indeksu wpisu, który chcemy zaktualizować. Łatwo go znaleźć za pomocą .findIndex () :
const indexOfListToUpdate = obj.get('elem').findIndex(listItem => { return listItem.get('name') === 'third'; }); obj = obj.setIn(['elem', indexOfListingToUpdate, 'count'], 4);
Mam nadzieję, że to pomoże!
źródło
{ }
środkufromJS( )
, bez nawiasów klamrowych nie jest to poprawny JS.var index = list.findIndex(item => item.name === "three") list = list.setIn([index, "count"], 4)
Wyjaśnienie
Aktualizowanie kolekcji Immutable.js zawsze zwraca nowe wersje tych kolekcji, pozostawiając oryginał niezmieniony. Z tego powodu nie możemy używać
list[2].count = 4
składni mutacji JavaScript . Zamiast tego musimy wywołać metody, podobnie jak w przypadku klas kolekcji Java.Zacznijmy od prostszego przykładu: tylko liczby na liście.
var arr = []; arr.push(2); arr.push(1); arr.push(2); arr.push(1); var counts = Immutable.List.of(arr);
Teraz, jeśli chcemy, aby zaktualizować 3rd element, zwykły tablica JS może wyglądać tak:
counts[2] = 4
. Ponieważ nie możemy użyć mutacji i musimy wywołać metodę, zamiast tego możemy użyć:counts.set(2, 4)
- oznacza to ustawienie wartości4
w indeksie2
.Głębokie aktualizacje
Jednak podany przykład zawiera zagnieżdżone dane. Nie możemy po prostu użyć
set()
na początkowej kolekcji.Kolekcje Immutable.js mają rodzinę metod, których nazwy kończą się na „In”, które umożliwiają wprowadzanie głębszych zmian w zagnieżdżonym zestawie. Większość popularnych metod aktualizacji ma powiązaną metodę „In”. Na przykład
set
jestsetIn
. Zamiast akceptować indeks lub klucz jako pierwszy argument, te metody „In” akceptują „ścieżkę klucza”. Ścieżka klucza to tablica indeksów lub kluczy, która ilustruje, jak dotrzeć do wartości, którą chcesz zaktualizować.W naszym przykładzie chciałeś zaktualizować element na liście pod indeksem 2, a następnie wartość w kluczu „count” w tym elemencie. Więc kluczowa ścieżka byłaby
[2, "count"]
. Drugi parametrsetIn
metody działa tak samoset
, jest to nowa wartość, którą chcemy tam umieścić, więc:list = list.setIn([2, "count"], 4)
Znalezienie właściwej ścieżki klucza
Idąc o krok dalej, powiedziałeś, że chcesz zaktualizować element, którego nazwa to „trzy”, co różni się od trzeciego elementu. Na przykład, może Twoja lista nie jest posortowana, a może element o nazwie „dwa” został wcześniej usunięty? Oznacza to, że najpierw musimy się upewnić, że faktycznie znamy prawidłową ścieżkę klucza! W tym celu możemy użyć
findIndex()
metody (która, nawiasem mówiąc, działa prawie dokładnie tak jak Array # findIndex ).Po znalezieniu indeksu na liście zawierającego element, który chcemy zaktualizować, możemy podać ścieżkę klucza do wartości, którą chcemy zaktualizować:
var index = list.findIndex(item => item.name === "three") list = list.setIn([index, "count"], 4)
NB:
Set
vsUpdate
Oryginalne pytanie dotyczy metod aktualizacji, a nie metod zestawu. Wyjaśnię drugi argument w tej funkcji (nazywany
updater
), ponieważ różni się odset()
. Podczas gdy drugi argumentset()
to nowa wartość, której szukamy, drugi argumentupdate()
to funkcja, która przyjmuje poprzednią wartość i zwraca nową, którą chcemy. NastępnieupdateIn()
jest wariantem „In”,update()
który akceptuje ścieżkę klucza.Załóżmy na przykład, że chcieliśmy mieć odmianę twojego przykładu, która nie tylko ustawia liczbę na
4
, ale zamiast tego zwiększa istniejącą liczbę, możemy zapewnić funkcję, która dodaje ją do istniejącej wartości:var index = list.findIndex(item => item.name === "three") list = list.updateIn([index, "count"], value => value + 1)
źródło
Nie potrzebujesz
updateIn
, co dotyczy tylko struktur zagnieżdżonych. Szukaszupdate
metody , która ma znacznie prostszą sygnaturę i dokumentację:co, jak sugerują
Map::update
dokumenty , jest „ równoważne z:list.set(index, updater(list.get(index, notSetValue)))
”.Nie tak działają listy. Musisz znać indeks elementu, który chcesz zaktualizować, lub musisz go wyszukać.
To powinno wystarczyć:
list = list.update(2, function(v) { return {id: v.id, name: v.name, count: 4}; });
źródło
Map
over aList
jest podejściem, które byś zasugerował? A może mówisz tak tylko dlatego, że OP powiedział „wybierz element według nazwy”, a nie „wybierz element według identyfikatora”?Użyj .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>
źródło
Bardzo podoba mi się to podejście ze strony 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);
źródło
Możesz użyć
map
:list = list.map((item) => { return item.get("name") === "third" ? item.set("count", 4) : item; });
Ale to będzie powtarzać się w całej kolekcji.
źródło