2015-04-12 2 views
100

Вот что официальные документы сказалКак обновить элемент внутри List с ImmutableJS?

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 где элемент с именем третьего имеют своего COUNT установлен в положение ?

+0

Похоже на машинопись для меня ... – Bergi

+36

Я не знаю, как это выглядит, но документация ужасна http://facebook.github.io/immutable-js/docs/#/List/update –

+59

Серьезная поддержка для в документации. Если цель этого синтаксиса - заставить меня чувствовать себя немым/неадекватным, определенным успехом. Если цель, однако, состоит в том, чтобы сообщить, как работает Immutable, хорошо ... – Owen

ответ

95

Наиболее подходящий случай использовать как findIndex и update методы.

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

P.S. Не всегда можно использовать Карты. Например. если имена не уникальны, и я хочу обновить все элементы с одинаковыми именами.

+1

Если вам нужны дубликаты имен, используйте мультимаксы - или карты с кортежами в качестве значений. – Bergi

+3

Не будет ли это непреднамеренно обновлять последний элемент списка, если в нем нет элемента с «тремя»? В этом случае findIndex() вернет -1, которое update() будет интерпретировать как индекс последнего элемента. –

+1

Нет, -1 не является последним элементом –

18

Вот что говорит официальные документы ... updateIn

Вам не нужно updateIn, что только для вложенных структур. Вы ищете update method, который имеет гораздо более простую подпись и документация:

Возвращает новый список с обновленной стоимости на индекс со значением вызова программы обновления с существующим значением возврата или notSetValue если индекс не было установлено.

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

, которые, как предполагают, Map::update docs, является "эквивалент: list.set(index, updater(list.get(index, notSetValue)))".

где элемент с именем "третьего"

Это не то, как работают списки. Вы должны знать индекс элемента, который хотите обновить, или вам нужно его искать.

Как обновить список, где элемент с именем третий имеет свой счет, равный 4?

Это следует сделать это:

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

«Это не то, как работают списки». Может быть. Но так работает бизнес-логика, и моя работа программиста заключается в том, чтобы перевести бизнес-логику в логику структуры данных. И ваш ответ не удовлетворяет этой логике. Итак, вы говорите, что сначала я должен знать индекс. Означает ли это, что я не могу сделать это в одном вызове метода? Должен ли я использовать 'findIndex' и только тогда' update'? –

+13

Нет, ваш выбор структуры данных не удовлетворяет бизнес-логике :-) Если вы хотите получить доступ к элементам по их имени, вы должны использовать карту вместо списка. – Bergi

+1

@bergi, правда? Итак, если у меня есть, скажем, список учеников в классе, и я хочу делать операции CRUD с данными этого студента, использование «Карты» над «списком» - это подход, который вы бы предложили?Или вы только говорите, что, поскольку OP сказал «выбрать элемент по имени», а не «выбрать элемент по id»? –

19

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

вам не хватает '{}' inside' fromJS() ', без привязок это недопустимо JS. – Andy

+1

Я бы не назвал возвращенный объект 'arr', поскольку это объект. только arr.elem - массив –

8

Использование .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>

+0

Спасибо за это - так много ужасных запутанных ответов здесь, реальный ответ: «если вы хотите изменить список, используйте карту». В этом весь смысл постоянных неизменных структур данных, вы не «обновляете» их, вы создаете новую структуру с обновленными значениями в ней; и это дешево, потому что немодифицированные элементы списка разделяются. – Korny

12
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», которые позволяют вам делать более глубокие изменения во вложенном наборе. Наиболее распространенные методы обновления имеют связанный метод «В». Например, для set есть setIn. Вместо того, чтобы принимать индекс или ключ в качестве первого аргумента, эти методы «В» принимают «ключевой путь». Ключевым путем является массив индексов или ключей, который иллюстрирует, как получить значение, которое вы хотите обновить.

В вашем примере вы хотели обновить элемент в списке по индексу 2, а затем значение в ключе «счет» внутри этого элемента. Таким образом, ключевой путь будет [2, "count"]. Второй параметр метода setIn работает точно так же, как set, это новое значение, которое мы хотим поставить там, так:

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

найти правильный ключ путь

Going один шаг дальше, вы на самом деле сказал вы хотели обновить элемент, где имя «три», которое отличается от третьего. Например, возможно, ваш список не отсортирован, или, возможно, элемент с именем «два» был удален ранее? Это означает, что сначала нам нужно убедиться, что мы действительно знаем правильный путь ключа! Для этого мы можем использовать метод findIndex() (который, кстати, работает почти точно как Array#findIndex).

После того, как мы обнаружили, индекс в списке, который имеет элемент, который мы хотим обновить, мы можем обеспечить ключ путь к значению мы хотим обновить:

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

NB: Set против Update

В исходном вопросе упоминаются методы обновления, а не установленные методы. Я объясню второй аргумент в этой функции (так называемый updater), так как он отличается от set(). Хотя второй аргумент set() - это новое значение, которое мы хотим, второй аргумент update() - это функция , которая принимает предыдущее значение и возвращает новое значение, которое мы хотим. Затем updateIn() является вариацией «В» update(), которая принимает ключевой путь.

Скажем, например, мы хотели вариацию вашего примера, который не только установить счетчик на 4, но вместо этого увеличивается существующий счет, мы могли бы обеспечить функцию, которая добавляет один к существующему значению:

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

вы должны зафиксировать http://stackoverflow.com/documentation/immutable.js – activedecay

-1

Вы можете использовать map:

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

Но это итерацию по всей коллекции.

 Смежные вопросы

  • Нет связанных вопросов^_^