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

时间:2022-10-11 22:03:53

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?

如何更新列表,其中名称为third的元素的计数设置为4?

6 个解决方案

#1


99  

The most appropriate case is to use both findIndex and update methods.

最合适的情况是使用findIndex和update方法。

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.

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

#2


21  

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!

希望能帮助到你!

#3


18  

Here is what official docs said… updateIn

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

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

您不需要updateIn,仅适用于嵌套结构。您正在寻找更新方法,它具有更简单的签名和文档:

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

#4


14  

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 = 4 mutation syntax. Instead we need to call methods, much like we might do with Java collection classes.

更新Immutable.js集合始终返回这些集合的新版本,保留原始集合。因此,我们不能使用JavaScript的列表[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 4 at the index 2.

现在,如果我们想要更新第3项,普通的JS数组可能看起来像:count [2] = 4.由于我们不能使用变异,需要调用方法,而是我们可以使用: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. Most common updating methods have a related "In" method. For example for set there 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”方法。例如,对于set,有setIn。这些“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 setIn method 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: Set vs 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 function which accepts the previous value and returns the new value we want. Then, updateIn() is the "In" variation of update() which accepts a key path.

原始问题提到了更新方法而不是set方法。我将解释该函数中的第二个参数(称为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 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)

#5


8  

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>

#6


-2  

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.

但这将遍及整个集合。

#1


99  

The most appropriate case is to use both findIndex and update methods.

最合适的情况是使用findIndex和update方法。

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.

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

#2


21  

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!

希望能帮助到你!

#3


18  

Here is what official docs said… updateIn

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

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

您不需要updateIn,仅适用于嵌套结构。您正在寻找更新方法,它具有更简单的签名和文档:

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

#4


14  

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 = 4 mutation syntax. Instead we need to call methods, much like we might do with Java collection classes.

更新Immutable.js集合始终返回这些集合的新版本,保留原始集合。因此,我们不能使用JavaScript的列表[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 4 at the index 2.

现在,如果我们想要更新第3项,普通的JS数组可能看起来像:count [2] = 4.由于我们不能使用变异,需要调用方法,而是我们可以使用: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. Most common updating methods have a related "In" method. For example for set there 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”方法。例如,对于set,有setIn。这些“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 setIn method 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: Set vs 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 function which accepts the previous value and returns the new value we want. Then, updateIn() is the "In" variation of update() which accepts a key path.

原始问题提到了更新方法而不是set方法。我将解释该函数中的第二个参数(称为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 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)

#5


8  

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>

#6


-2  

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.

但这将遍及整个集合。