Làm cách nào để cập nhật phần tử bên trong Danh sách với ImmutableJS?


129

Đây là những gì tài liệu chính thức nó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>

Không có cách nào nhà phát triển web bình thường (không phải lập trình viên chức năng) sẽ hiểu điều đó!

Tôi có trường hợp khá đơn giản (đối với phương pháp không chức năng).

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

Làm thế nào tôi có thể cập nhật listnơi phần tử với tên thứ ba có nó đếm các thiết lập để 4 ?


43
Tôi không biết nó trông như thế nào, nhưng tài liệu rất tệ facebook.github.io/immutable-js/docs/#/List/update
Vitalii Korsakov

71
Upvote nghiêm trọng cho các đào tại các tài liệu. Nếu mục tiêu của cú pháp đó là làm cho tôi cảm thấy ngu ngốc / không đầy đủ, thành công nhất định. Tuy nhiên, nếu mục tiêu là truyền đạt cách thức hoạt động của Bất biến, thì ...
Owen

9
Tôi phải đồng ý, tôi thấy tài liệu về bất biến là nghiêm trọng. Giải pháp được chấp nhận cho điều này sử dụng một phương thức find Index () mà tôi thậm chí không thấy trong các tài liệu.
RichardForrester

3
Tôi muốn họ đưa ra một ví dụ cho từng chức năng thay vì những điều đó.
RedGiant

4
Đã đồng ý. Các tài liệu phải được viết bởi một nhà khoa học trong một bong bóng, không phải bởi ai đó muốn hiển thị một số ví dụ đơn giản. Các tài liệu cần một số tài liệu. Ví dụ, tôi thích tài liệu gạch dưới, dễ dàng hơn nhiều để xem các ví dụ thực tế.
ReduxDJ

Câu trả lời:


119

Trường hợp thích hợp nhất là sử dụng cả hai findIndexupdatephương pháp.

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

PS Không phải lúc nào cũng có thể sử dụng Bản đồ. Ví dụ: nếu tên không phải là duy nhất và tôi muốn cập nhật tất cả các mục có cùng tên.


1
Nếu bạn cần tên trùng lặp, hãy sử dụng nhiều ảnh - hoặc bản đồ với các bộ dữ liệu làm giá trị.
Bergi

5
Điều này sẽ vô tình cập nhật phần tử cuối cùng của danh sách nếu không có phần tử nào có "ba" làm tên? Trong trường hợp đó, find Index () sẽ trả về -1, mà update () sẽ hiểu là chỉ mục của phần tử cuối cùng.
Sam Storie

1
Không, -1 không phải là yếu tố cuối cùng
Vitalii Korsakov

9
@Sam Storie nói đúng. Từ các tài liệu: "chỉ mục có thể là số âm, chỉ mục trở lại từ cuối Danh sách. V.update (-1) cập nhật mục cuối cùng trong Danh sách." facebook.github.io/immutable-js/docs/#/List/update
Gabriel Ferraz

1
Tôi không thể gọi item.set vì đối với tôi, mục này không phải là bất biến, tôi phải ánh xạ mục đó trước, đặt cuộc gọi và sau đó chuyển đổi lại thành đối tượng. Vì vậy, với ví dụ này, hàm (item) {return Map (item) .set ("Count", 4) .toObject (); }
sycle

36

Với .setIn () bạn có thể làm tương tự:

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

Nếu chúng tôi không biết chỉ mục của mục chúng tôi muốn cập nhật. Thật dễ dàng để tìm thấy nó bằng cách sử dụng .findIndex () :

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

Hy vọng nó giúp!


1
bạn đang thiếu { }bên trong fromJS( ), không có dấu ngoặc nhọn, nó không hợp lệ.
Andy

1
Tôi sẽ không gọi đối tượng trả về là 'mảng' vì nó là một đối tượng. chỉ Array.elem là một mảng
Nir O.

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

Giải trình

Cập nhật các bộ sưu tập Immutable.js luôn trả về các phiên bản mới của các bộ sưu tập đó không thay đổi bản gốc. Do đó, chúng tôi không thể sử dụng list[2].count = 4cú pháp đột biến của JavaScript . Thay vào đó, chúng ta cần gọi các phương thức, giống như chúng ta có thể làm với các lớp bộ sưu tập Java.

Hãy bắt đầu với một ví dụ đơn giản hơn: chỉ đếm trong một danh sách.

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

Bây giờ nếu chúng tôi muốn cập nhật mục thứ 3, một mảng JS đơn giản có thể trông như : counts[2] = 4. Vì chúng ta không thể sử dụng đột biến và cần gọi một phương thức, thay vào đó chúng ta có thể sử dụng: counts.set(2, 4)- điều đó có nghĩa là đặt giá trị 4tại chỉ mục 2.

Cập nhật sâu

Ví dụ bạn đưa ra có dữ liệu lồng nhau mặc dù. Chúng tôi không thể chỉ sử dụng set()trên bộ sưu tập ban đầu.

Các bộ sưu tập của Immutable.js có một nhóm các phương thức có tên kết thúc bằng "In" cho phép bạn thực hiện các thay đổi sâu hơn trong một tập hợp lồng nhau. Hầu hết các phương pháp cập nhật phổ biến đều có phương thức "Trong" liên quan. Ví dụ cho setsetIn. Thay vì chấp nhận một chỉ mục hoặc khóa làm đối số đầu tiên, các phương thức "Trong" này chấp nhận "đường dẫn chính". Đường dẫn khóa là một mảng các chỉ mục hoặc khóa minh họa cách lấy giá trị bạn muốn cập nhật.

Trong ví dụ của bạn, bạn muốn cập nhật mục trong danh sách ở chỉ mục 2, và sau đó giá trị tại khóa "đếm" trong mục đó. Vì vậy, con đường chính sẽ là [2, "count"]. Tham số thứ hai cho setInphương thức hoạt động giống như set, đó là giá trị mới mà chúng tôi muốn đặt ở đó, vì vậy:

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

Tìm đúng đường dẫn chính

Đi thêm một bước nữa, bạn thực sự nói rằng bạn muốn cập nhật mục có tên là "ba" , khác với mục thứ 3. Ví dụ: có thể danh sách của bạn không được sắp xếp hoặc có thể có mục "hai" đã bị xóa trước đó? Điều đó có nghĩa là trước tiên chúng ta cần đảm bảo rằng chúng ta thực sự biết đường dẫn chính xác! Để làm điều này, chúng ta có thể sử dụng findIndex()phương thức (bằng cách này, hoạt động gần như chính xác như Array # find Index ).

Khi chúng tôi đã tìm thấy chỉ mục trong danh sách có mục chúng tôi muốn cập nhật, chúng tôi có thể cung cấp đường dẫn chính đến giá trị chúng tôi muốn cập nhật:

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

NB: SetvsUpdate

Câu hỏi ban đầu đề cập đến các phương thức cập nhật hơn là các phương thức thiết lập. Tôi sẽ giải thích đối số thứ hai trong hàm đó (được gọi updater), vì nó khác với set(). Trong khi đối số thứ hai set()là giá trị mới mà chúng ta muốn, thì đối số thứ hai update()hàm chấp nhận giá trị trước đó và trả về giá trị mới mà chúng ta muốn. Sau đó, updateIn()là biến thể "Trong" update()chấp nhận đường dẫn chính.

Ví dụ: chúng tôi muốn một biến thể của ví dụ của bạn không chỉ đặt số đếm thành 4, mà thay vào đó, tăng số đếm hiện có, chúng tôi có thể cung cấp một hàm thêm một giá trị vào giá trị hiện có:

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

19

Đây là những gì tài liệu chính thức nói updateIn

Bạn không cần updateIn, chỉ dành cho các cấu trúc lồng nhau. Bạn đang tìm kiếm updatephương thức có chữ ký và tài liệu đơn giản hơn nhiều:

Trả về một Danh sách mới có giá trị được cập nhật tại chỉ mục với giá trị trả về của trình cập nhật cuộc gọi với giá trị hiện có hoặc not setValue nếu chỉ mục không được đặt.

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

mà, như các Map::updatetài liệu đề xuất, là " tương đương với:list.set(index, updater(list.get(index, notSetValue))) ".

trong đó phần tử có tên "thứ ba"

Đó không phải là cách danh sách hoạt động. Bạn phải biết chỉ mục của yếu tố mà bạn muốn cập nhật hoặc bạn phải tìm kiếm nó.

Làm cách nào tôi có thể cập nhật danh sách trong đó phần tử có tên thứ ba được đặt thành 4?

Điều này nên làm điều đó:

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

14
Không, lựa chọn cấu trúc dữ liệu của bạn không thỏa mãn logic nghiệp vụ :-) Nếu bạn muốn truy cập các phần tử theo tên của chúng, bạn nên sử dụng bản đồ thay vì danh sách.
Bergi

1
@bergi, thật sao? Vì vậy, nếu tôi có, giả sử, một danh sách các sinh viên trong một lớp và tôi muốn thực hiện các thao tác CRUD trên dữ liệu sinh viên này, sử dụng Maphơn một Listcách tiếp cận bạn sẽ đề xuất? Hay bạn chỉ nói vậy bởi vì OP đã nói "chọn mục theo tên" chứ không phải "chọn mục theo id"?
David Gilbertson

2
@DavidGilbertson: CRUD? Tôi muốn đề xuất một cơ sở dữ liệu :-) Nhưng vâng, một id-> bản đồ sinh viên nghe có vẻ phù hợp hơn một danh sách, trừ khi bạn có một đơn đặt hàng trên chúng và muốn chọn chúng theo chỉ mục.
Bergi

1
@bergi Tôi đoán chúng tôi sẽ đồng ý không đồng ý, tôi nhận được nhiều dữ liệu từ API dưới dạng mảng các thứ có ID, nơi tôi cần cập nhật những thứ đó bằng ID. Do đó, đây là một Mảng JS, và trong suy nghĩ của tôi, nên là một danh sáchJS bất biến.
David Gilbertson

1
@DavidGilbertson: Tôi nghĩ rằng các API đó lấy mảng JSON cho các bộ - vốn không được sắp xếp rõ ràng.
Bergi

12

Sử dụng .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
Cảm ơn vì điều đó - rất nhiều câu trả lời khủng khiếp ở đây, câu trả lời thực sự là "nếu bạn muốn thay đổi danh sách, hãy sử dụng bản đồ". Đó là toàn bộ quan điểm của các cấu trúc dữ liệu bất biến liên tục, bạn không "cập nhật" chúng, bạn tạo một cấu trúc mới với các giá trị được cập nhật trong đó; và nó rẻ để làm như vậy bởi vì các yếu tố danh sách chưa sửa đổi được chia sẻ.
Korny

2

Tôi thực sự thích cách tiếp cận này từ trang web của 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);

-2

Bạn có thể sử dụng map:

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

Nhưng điều này sẽ lặp đi lặp lại trên toàn bộ bộ sưu tập.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.