ImmutableJS로 List 내의 요소를 업데이트하는 방법은 무엇입니까?


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>

정상적인 웹 개발자 (기능 프로그래머가 아님)가 그것을 이해할 수있는 방법은 없습니다!

비 기능적 접근법의 경우 매우 간단합니다.

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이름이 세 번째 인 요소 의 개수4로 설정된 위치를 어떻게 업데이트 합니까?


43
어떻게
Vitalii Korsakov

71
문서에서 발굴에 대한 진지한지지. 그 구문의 목표가 내가 바보 / 부적절하고 확실한 성공을 느끼게하는 것입니다. 그러나 목표가 불변의 작동 방식을 전달하는 것이라면 ...
Owen

9
동의해야합니다 .immutable.js에 대한 문서가 심각하게 실망 스럽습니다. 이것에 허용되는 솔루션은 문서에서 전혀 보지 못하는 findIndex () 메소드를 사용합니다.
RichardForrester

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

추신 : 항상지도를 사용할 수있는 것은 아닙니다. 예를 들어 이름이 고유하지 않고 같은 이름으로 모든 항목을 업데이트하고 싶습니다.


1
중복 이름이 필요한 경우 멀티 맵을 사용하거나 튜플을 값으로 사용하여 맵을 사용하십시오.
Bergi

5
이름이 "three"인 요소가없는 경우 목록의 마지막 요소가 실수로 업데이트되지 않습니까? 이 경우 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을 호출 할 수 없었습니다. 항목을 먼저 맵핑하고 호출 세트를 다시 객체로 다시 변환해야했습니다. 따라서이 예제에서 function (item) {return Map (item) .set ( "count", 4) .toObject (); }
syclee

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하지 않은 괄호없이.
Andy

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

이제 세 번째 항목을 업데이트하려는 경우 일반 JS 배열은 다음과 같습니다 counts[2] = 4. 우리가 메소드를 호출하는 돌연변이, 그리고 필요를 사용할 수 없기 때문에, 대신에 우리가 사용할 수 있습니다 : counts.set(2, 4)- 수단 값을 설정하는 것이 4인덱스에서2 .

깊은 업데이트

당신이 준 예제는 중첩 된 데이터를 가지고 있습니다. 우리는 단지 사용할 수 없습니다set()초기 컬렉션에는 .

Immutable.js 컬렉션에는 이름이 "In"으로 끝나는 메서드 모음이 있으므로 중첩 된 집합을 더 깊이 변경할 수 있습니다. 가장 일반적인 업데이트 방법에는 관련 "입력"방법이 있습니다. 예를 들어 대한 setsetIn . 이러한 "In"메소드는 색인 또는 키를 첫 번째 인수로 승인하는 대신 "키 경로"를 승인합니다. 키 경로는 업데이트하려는 값에 도달하는 방법을 보여주는 인덱스 또는 키 배열입니다.

예를 들어, 색인 2의 목록에서 항목을 업데이트 한 다음 해당 항목 내의 "count"키의 값을 업데이트하려고합니다. 따라서 핵심 경로는입니다 [2, "count"]. setIn메소드 의 두 번째 매개 변수는 다음 과 같이 작동 set합니다.이 값은 우리가 거기에 넣고 싶은 새로운 값이므로 다음과 같습니다.

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

올바른 키 경로 찾기

한 단계 더 나아가서 실제로 이름이 "세" 인 항목을 세 번째 항목과 다른 항목으로 업데이트하고 싶다고 말했습니다 . 예를 들어 목록이 정렬되지 않았거나 "two"라는 항목이 이전에 제거 되었습니까? 즉, 먼저 올바른 키 경로를 실제로 알아야합니다. 이를 위해 우리는 findIndex()메소드 를 사용할 수 있습니다 ( 어쨌든 Array # findIndex 와 거의 동일하게 작동합니다) ).

목록에서 업데이트하려는 항목이있는 인덱스를 찾았 으면 업데이트 할 값의 키 경로를 제공 할 수 있습니다.

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

NB : SetvsUpdate

원래 질문에는 set 메소드가 아닌 업데이트 메소드가 언급되어 있습니다. 이 함수의 두 번째 인수 ()는 설명과 updater다르므로 설명하겠습니다 set(). 두 번째 인수 set()는 원하는 새 값 이지만 두 번째 인수 는 이전 값을 허용하고 원하는 새 값을 반환 update()하는 함수 입니다. 그런 다음 updateIn()"In"변형은 update()키 경로를 허용합니다.

예를 들어 카운트를로 설정하지 않고 기존 카운트 4증가 시키는 예제의 변형을 원한다면 기존 값에 1을 더하는 함수를 제공 할 수 있습니다.

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

19

공식 문서는 다음과 같습니다. updateIn

updateIn중첩 구조에 대해서만 필요 합니다. 서명과 문서가 훨씬 간단한 update메소드를 찾고 있습니다.

기존 값으로 updater를 호출하는 반환 값 또는 index가 설정되지 않은 경우 notSetValue를 반환하여 색인에 업데이트 된 값이있는 새 List를 반환합니다.

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

하는 경우, AS Map::update문서는 제안이다 " 동일합니다 :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};
});

14
아니오, 선택한 데이터 구조가 비즈니스 로직을 충족하지 않습니다 :-) 이름으로 요소에 액세스하려면 목록 대신 맵을 사용해야합니다.
Bergi

1
@bergi, 정말로? 예를 들어 수업에 학생 목록이 있고이 학생 데이터에 대해 CRUD 작업을 수행하고 싶다면 Mapover를 사용하는 List것이 좋습니다. 아니면 OP가 "ID로 항목 선택"이 아니라 "이름으로 항목 선택"이라고 말했습니까?
David Gilbertson

2
@DavidGilbertson : CRUD? 나는 데이터베이스를 제안 할 것이다 :-) 그러나 네, 당신이 그들에 대한 주문을받지 않고 색인별로 선택하고 싶지 않다면 id-> 학생 맵이 목록보다 더 적절하게 들립니다.
Bergi

1
@bergi 우리가 동의하지 않을 것이라고 생각합니다 .ID로 물건을 업데이트 해야하는 ID를 가진 것들의 배열 형태로 API에서 많은 데이터를 다시 얻습니다. 이것은 JS 배열이므로 제 생각에는 불변의 JS 목록이어야합니다.
David Gilbertson

1
@DavidGilbertson : 그 API는 세트에 대해 JSON 배열을 취한다고 생각합니다.
Bergi

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

2

나는 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

당신은 사용할 수 있습니다 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.