简体   繁体   English

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

[英]How to update element inside List with ImmutableJS?

Here is what official docs said 这是官方文档所说的

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>

There is no way normal web developer (not functional programmer) would understand that! 普通的Web开发人员(非功能程序员)无法理解这一点!

I have pretty simple (for non-functional approach) case. 我非常简单(对于非功能性方法)的情况。

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

How can I update list where element with name third have its count set to 4 ? 如何更新list ,其中名称为third的元素的计数设置为4

The most appropriate case is to use both findIndex and update methods. 最合适的情况是使用findIndexupdate方法。

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

PS It's not always possible to use Maps. PS并不总是可以使用地图。 Eg if names are not unique and I want to update all items with the same names. 例如,如果名称不唯一,我想更新具有相同名称的所有项目。

With .setIn() you can do the same: 使用.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);

If we don't know the index of the entry we want to update. 如果我们不知道要更新的条目的索引。 It's pretty easy to find it using .findIndex() : 使用.findIndex()很容易找到它:

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

Hope it helps! 希望能帮助到你!

Here is what official docs said… updateIn 这是官方文档说的...... updateIn

You don't need updateIn , which is for nested structures only. 您不需要updateIn ,仅适用于嵌套结构。 You are looking for the update method , which has a much simpler signature and documentation: 您正在寻找update方法 ,它具有更简单的签名和文档:

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. 返回一个新的List,其索引处的更新值为使用现有值调用updater的返回值,如果未设置index,则返回notSetValue。

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

which, as the Map::update docs suggest, is " equivalent to: list.set(index, updater(list.get(index, notSetValue))) ". 正如Map::update文档所暗示的那样,它“ 等同于: list.set(index, updater(list.get(index, notSetValue))) ”。

where element with name "third" 其中名称为“third”的元素

That's not how lists work. 这不是列表的工作方式。 You have to know the index of the element that you want to update, or you have to search for it. 您必须知道要更新的元素的索引,或者您必须搜索它。

How can I update list where element with name third have its count set to 4? 如何更新列表,其中名称为third的元素的计数设置为4?

This should do it: 这应该这样做:

list = list.update(2, function(v) {
    return {id: v.id, name: v.name, count: 4};
});
var index = list.findIndex(item => item.name === "three")
list = list.setIn([index, "count"], 4)

Explanation 说明

Updating Immutable.js collections always return new versions of those collections leaving the original unchanged. 更新Immutable.js集合始终返回这些集合的新版本,而原始集合保持不变。 Because of that, we can't use JavaScript's list[2].count = 4 mutation syntax. 因此,我们不能使用JavaScript的list[2].count = 4变异语法。 Instead we need to call methods, much like we might do with Java collection classes. 相反,我们需要调用方法,就像我们可能使用Java集合类一样。

Let's start with a simpler example: just the counts in a list. 让我们从一个更简单的例子开始:只是列表中的计数。

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

Now if we wanted to update the 3rd item, a plain JS array might look like: counts[2] = 4 . 现在,如果我们想要更新第3项,那么普通的JS数组可能看起来像: counts[2] = 4 Since we can't use mutation, and need to call a method, instead we can use: counts.set(2, 4) - that means set the value 4 at the index 2 . 由于我们不能使用变异,并且需要调用方法,而是我们可以使用: counts.set(2, 4) - 这意味着在索引2处设置值4

Deep updates 深度更新

The example you gave has nested data though. 您给出的示例包含嵌套数据。 We can't just use set() on the initial collection. 我们不能只在初始集合上使用set()

Immutable.js collections have a family of methods with names ending with "In" which allow you to make deeper changes in a nested set. Immutable.js集合有一系列方法,名称以“In”结尾,允许您在嵌套集中进行更深层次的更改。 Most common updating methods have a related "In" method. 最常见的更新方法具有相关的“In”方法。 For example for set there is setIn . 例如,对于setsetIn Instead of accepting an index or a key as the first argument, these "In" methods accept a "key path". 这些“In”方法不接受索引或键作为第一个参数,而是接受“关键路径”。 The key path is an array of indexes or keys that illustrates how to get to the value you wish to update. 关键路径是索引或键的数组,用于说明如何获取要更新的值。

In your example, you wanted to update the item in the list at index 2, and then the value at the key "count" within that item. 在您的示例中,您希望更新索引2中列表中的项目,然后更新该项目中键“count”的值。 So the key path would be [2, "count"] . 所以关键路径是[2, "count"] The second parameter to the setIn method works just like set , it's the new value we want to put there, so: setIn方法的第二个参数就像set一样工作,它是我们想要放在那里的新值,所以:

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

Finding the right key path 找到正确的关键路径

Going one step further, you actually said you wanted to update the item where the name is "three" which is different than just the 3rd item. 更进一步,你实际上说你想要更新名称为“3”的项目,这与第3项不同。 For example, maybe your list is not sorted, or perhaps there the item named "two" was removed earlier? 例如,您的列表可能没有排序,或者可能之前删除了名为“two”的项目? That means first we need to make sure we actually know the correct key path! 这意味着首先我们需要确保我们确实知道正确的关键路径! For this we can use the findIndex() method (which, by the way, works almost exactly like Array#findIndex ). 为此我们可以使用findIndex()方法(顺便说一句,它的工作方式几乎与Array#findIndex一样 )。

Once we've found the index in the list which has the item we want to update, we can provide the key path to the value we wish to update: 一旦我们在列表中找到了包含我们想要更新的项目的索引,我们就可以提供我们希望更新的值的关键路径:

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

NB: Set vs Update 注意: SetUpdate

The original question mentions the update methods rather than the set methods. 原始问题提到了更新方法而不是set方法。 I'll explain the second argument in that function (called updater ), since it's different from set() . 我将解释该函数中的第二个参数(称为updater ),因为它与set()不同。 While the second argument to set() is the new value we want, the second argument to update() is a function which accepts the previous value and returns the new value we want. 而第二个参数set()是我们希望新的价值,第二个参数来update()是接受于前值和返回,我们希望新的值的函数 Then, updateIn() is the "In" variation of update() which accepts a key path. 然后, updateIn()update()的“In”变体,它接受一个键路径。

Say for example we wanted a variation of your example that didn't just set the count to 4 , but instead incremented the existing count, we could provide a function which adds one to the existing value: 比方说,我们想要一个不仅将计数设置为4示例变体,而是增加现有计数,我们可以提供一个函数,将现有值加1:

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

Use .map() 使用.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> 

I really like this approach from the thomastuts website: 我非常喜欢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);

You can use map : 你可以使用map

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

But this will iterate over the entire collection. 但这将遍及整个集合。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM