如何用 ImmutableJS 更新 List 中的元素?

官方文件是这么说的

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>

一般的 Web 开发人员(不是函数式程序员)是不可能理解这一点的!

我有一个非常简单的(非功能性方法)案例。

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

如何更新名为 第三的元素的 计数设置为 4list

88088 次浏览

官方文件是这么说的... updateIn

您不需要 updateIn,它仅用于嵌套结构。您要找的是 update方法,它的签名和文档要简单得多:

返回一个新 List,其在索引处的更新值与返回值一起 使用现有值调用 updater,如果 没有设置索引。

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

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

最合适的情况是同时使用 findIndexupdate方法。

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

另外,使用地图并不总是可行的。例如,如果名称不是唯一的,我想更新所有项目的相同名称。

对于 . 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);

希望能有帮助!

使用 。地图()

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>

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)-这意味着在 2索引处设置值 4

深度报告

但是你给出的例子有嵌套的数据,我们不能只在初始集合中使用 set()

Js 集合有一系列以“ In”结尾的方法,这些方法允许您在嵌套集合中进行更深入的更改。大多数常见的更新方法都有一个相关的“ In”方法。例如,对于 set,有 setIn。这些“ In”方法不接受索引或键作为第一个参数,而是接受“键路径”。键路径是一个索引或键数组,用于说明如何获取要更新的值。

在您的示例中,您希望更新列表中索引2处的项,然后更新该项中键“ count”处的值。所以关键路径是 [2, "count"]setIn方法的第二个参数的工作原理与 set类似,它是我们想要放在那里的新值,因此:

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

找到正确的键路径

更进一步,你实际上说你想更新的项目,其中 名字是“三”不同于只是第三个项目。例如,可能您的列表没有排序,或者名为“ two”的项在那里被删除了?这意味着首先我们需要确保我们确实知道正确的键路径!为此,我们可以使用 findIndex()方法(顺便说一下,它的工作方式几乎与 数组 # findIndex完全一样)。

一旦我们在列表中找到了包含我们想要更新的项目的索引,我们就可以提供我们想要更新的值的键路径:

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

注意: Set vs Update

最初的问题提到了 update 方法,而不是 set 方法。我将解释该函数中的第二个参数(称为 updater) ,因为它不同于 set()set()的第二个参数是我们想要的新值,而 update()的第二个参数是 功能,它接受以前的值并返回我们想要的新值。然后,updateIn()update()的“ In”变体,它接受一个关键路径。

例如,我们想要你的例子的一个变体,不只是设置计数为 4,而是 增加现有的计数,我们可以提供一个函数,增加一个现有的值:

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

你可以使用 map:

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

但是这将遍历整个集合。

我真的很喜欢 托马斯塔茨网站上的这个方法:

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