如何使用 ImmutableJS 更新 List 中的元素?
How to update element inside List with 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>
普通的 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);
如何更新 list
,其中名称为 third 的元素的 count 设置为 4 ?
Here is what official docs said… updateIn
您不需要 updateIn
,它仅适用于嵌套结构。您正在寻找 update
method,它具有更简单的签名和文档:
Returns a new List with an updated value at index with the return
value of calling updater with the existing value, or notSetValue if
index was not set.
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)))
”。
where element with name "third"
列表不是这样工作的。您必须知道要更新的元素的索引,或者您必须搜索它。
How can I update list where element with name third have its count set to 4?
应该这样做:
list = list.update(2, function(v) {
return {id: v.id, name: v.name, count: 4};
});
最合适的情况是同时使用findIndex
和update
方法。
list = list.update(
list.findIndex(function(item) {
return item.get("name") === "third";
}), function(item) {
return item.set("count", 4);
}
);
P.S。并不总是可以使用地图。例如。如果名称不唯一并且我想更新所有具有相同名称的项目。
使用 .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);
希望对您有所帮助!
使用.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>
var index = list.findIndex(item => item.name === "three")
list = list.setIn([index, "count"], 4)
说明
更新 Immutable.js 集合始终 return 这些集合的新版本保持原始不变。因此,我们不能使用 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()
。
Immutable.js 集合有一系列名称以 "In" 结尾的方法,允许您在嵌套集合中进行更深层次的更改。最常见的更新方法有一个相关的 "In" 方法。例如 set
有 setIn
。这些 "In" 方法不接受索引或键作为第一个参数,而是接受 "key path"。键路径是一个索引或键数组,说明如何获取您希望更新的值。
在您的示例中,您想要更新列表中索引 2 处的项目,然后更新该项目中键 "count" 处的值。所以关键路径是[2, "count"]
。 setIn
方法的第二个参数就像 set
一样工作,它是我们要放在那里的新值,所以:
list = list.setIn([2, "count"], 4)
找到正确的关键路径
更进一步,您实际上说您想要更新 名称为 "three" 的项目,它与第三个项目不同。例如,您的列表可能未排序,或者名称为 "two" 的项目之前已被删除?这意味着首先我们需要确保我们确实知道正确的密钥路径!为此,我们可以使用 findIndex()
方法(顺便说一句,它的工作方式几乎与 Array#findIndex 完全相同)。
一旦我们在列表中找到包含我们要更新的项目的索引,我们就可以提供指向我们要更新的值的键路径:
var index = list.findIndex(item => item.name === "three")
list = list.setIn([index, "count"], 4)
注意:Set
对比 Update
原题提到更新方法而不是设置方法。我将解释该函数中的第二个参数(称为 updater
),因为它不同于 set()
。 set()
的第二个参数是我们想要的新值,而 update()
的第二个参数是一个 函数 ,它接受先前的值和 return这就是我们想要的新价值。然后,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;
});
但这会遍历整个集合。
我非常喜欢 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);
官方文档是这么说的
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);
如何更新 list
,其中名称为 third 的元素的 count 设置为 4 ?
Here is what official docs said…
updateIn
您不需要 updateIn
,它仅适用于嵌套结构。您正在寻找 update
method,它具有更简单的签名和文档:
Returns a new List with an updated value at index with the return value of calling updater with the existing value, or notSetValue if index was not set.
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)))
”。
where element with name "third"
列表不是这样工作的。您必须知道要更新的元素的索引,或者您必须搜索它。
How can I update list where element with name third have its count set to 4?
应该这样做:
list = list.update(2, function(v) {
return {id: v.id, name: v.name, count: 4};
});
最合适的情况是同时使用findIndex
和update
方法。
list = list.update(
list.findIndex(function(item) {
return item.get("name") === "third";
}), function(item) {
return item.set("count", 4);
}
);
P.S。并不总是可以使用地图。例如。如果名称不唯一并且我想更新所有具有相同名称的项目。
使用 .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);
希望对您有所帮助!
使用.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>
var index = list.findIndex(item => item.name === "three")
list = list.setIn([index, "count"], 4)
说明
更新 Immutable.js 集合始终 return 这些集合的新版本保持原始不变。因此,我们不能使用 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()
。
Immutable.js 集合有一系列名称以 "In" 结尾的方法,允许您在嵌套集合中进行更深层次的更改。最常见的更新方法有一个相关的 "In" 方法。例如 set
有 setIn
。这些 "In" 方法不接受索引或键作为第一个参数,而是接受 "key path"。键路径是一个索引或键数组,说明如何获取您希望更新的值。
在您的示例中,您想要更新列表中索引 2 处的项目,然后更新该项目中键 "count" 处的值。所以关键路径是[2, "count"]
。 setIn
方法的第二个参数就像 set
一样工作,它是我们要放在那里的新值,所以:
list = list.setIn([2, "count"], 4)
找到正确的关键路径
更进一步,您实际上说您想要更新 名称为 "three" 的项目,它与第三个项目不同。例如,您的列表可能未排序,或者名称为 "two" 的项目之前已被删除?这意味着首先我们需要确保我们确实知道正确的密钥路径!为此,我们可以使用 findIndex()
方法(顺便说一句,它的工作方式几乎与 Array#findIndex 完全相同)。
一旦我们在列表中找到包含我们要更新的项目的索引,我们就可以提供指向我们要更新的值的键路径:
var index = list.findIndex(item => item.name === "three")
list = list.setIn([index, "count"], 4)
注意:Set
对比 Update
原题提到更新方法而不是设置方法。我将解释该函数中的第二个参数(称为 updater
),因为它不同于 set()
。 set()
的第二个参数是我们想要的新值,而 update()
的第二个参数是一个 函数 ,它接受先前的值和 return这就是我们想要的新价值。然后,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;
});
但这会遍历整个集合。
我非常喜欢 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);