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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/29589753/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 03:38:06  来源:igfitidea点击:

How to update element inside List with ImmutableJS?

javascriptfunctional-programmingimmutable.js

提问by Vitalii Korsakov

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 listwhere element with name thirdhave its countset to 4?

如何更新list名称为third 的元素的计数设置为4 的位置

采纳答案by Vitalii Korsakov

The most appropriate case is to use both findIndexand updatemethods.

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

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

P.S. It's not always possible to use Maps. E.g. if names are not unique and I want to update all items with the same names.

PS 并非总是可以使用地图。例如,如果名称不是唯一的并且我想更新具有相同名称的所有项目。

回答by Albert Olivé

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!

希望能帮助到你!

回答by Lee Byron

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. Because of that, we can't use JavaScript's list[2].count = 4mutation syntax. Instead we need to call methods, much like we might do with Java collection classes.

更新 Immutable.js 集合总是返回这些集合的新版本,而原始版本保持不变。因此,我们不能使用 JavaScript 的list[2].count = 4突变语法。相反,我们需要调用方法,就像我们可能对 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. 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 4at the index 2.

现在,如果我们想更新的第三个项目,一个普通的JS数组可能看起来像:counts[2] = 4。由于我们不能使用mutation,并且需要调用一个方法,我们可以使用:counts.set(2, 4)- 这意味着4在 index 处设置值2

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. Most common updating methods have a related "In" method. For example for setthere is setIn. Instead of accepting an index or a key as the first argument, these "In" methods accept a "key path". The key path is an array of indexes or keys that illustrates how to get to the value you wish to update.

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

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. So the key path would be [2, "count"]. The second parameter to the setInmethod works just like set, it's the new value we want to put there, so:

在您的示例中,您想更新列表中索引 2 处的项目,然后更新该项目中键“count”处的值。所以关键路径是[2, "count"]. 该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. For example, maybe your list is not sorted, or perhaps there the item named "two" was removed earlier? 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).

更进一步,您实际上说要更新名称为“三”的项目,这与仅第三项不同。例如,您的列表可能未排序,或者名为“two”的项目可能较早被删除?这意味着首先我们需要确保我们确实知道正确的密钥路径!为此,我们可以使用该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: Setvs Update

注意:Set对比Update

The original question mentions the update methods rather than the set methods. I'll explain the second argument in that function (called updater), since it's different from set(). While the second argument to set()is the new value we want, the second argument to update()is a functionwhich accepts the previous value and returns the new value we want. Then, updateIn()is the "In" variation of update()which accepts a key path.

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

Say for example we wanted a variation of your example that didn't just set the count to 4, but instead incrementedthe existing count, we could provide a function which adds one to the existing value:

举例来说,我们想要一个示例的变体,它不仅将计数设置为4,而是增加现有计数,我们可以提供一个将现有值加一的函数:

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

回答by Bergi

Here is what official docs said… updateIn

这是官方文档所说的...... updateIn

You don't need updateIn, which is for nested structures only. You are looking for the updatemethod, which has a much simpler signature and documentation:

您不需要updateIn,它仅适用于嵌套结构。您正在寻找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.

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

返回一个新的 List,在 index 处具有更新的值,返回值是使用现有值调用 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::updatedocssuggest, 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"

其中名称为“第三个”的元素

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?

如何更新名称为第三个元素的计数设置为 4 的列表?

This should do it:

这应该这样做:

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

回答by Meistro

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>

回答by Dryden Williams

I really like this approach from the thomastutswebsite:

我真的很喜欢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);

回答by Aldo Porcallo

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.

但这将遍历整个集合。