更新ImmutableJS地图

时间:2017-07-18 02:33:26

标签: ecmascript-6 immutable.js

我正在尝试根据一些传入的分页数据更新Immutable.Map,在我的数据结构中构建顶级索引。

传入的数据如下所示:

[
  {
    identifier: ADFASD,
    tags: [ tag1, tag2]
  },  {
    identifier: GGHYX,
    tags: [ tag2, tag3]
  },
]

期望的结果

{
  taggedArticleList: {
     tag1: [ADFASD],
     tag2: [ADFASD, GGHYX],
     tag3: [GGHYX],
  }
}

我正在重构的当前JS函数(有效):

let taggedArticleList = {}
action.data.response.results.map((article) => {
    article.tags.map((tag) => {
        let key = taggedArticleList[tag] || (taggedArticleList[tag] = [];
        key.article.push(article.identifier)
    });
});
return taggedArticleList

我目前的ImmutableJS尝试:

.update('taggedArticleList', taggedArticleList =>
    Immutable.Map(
        Immutable.fromJS(action.data.response.results).map(
            article =>
                article.get('tags').map(
                    tag =>
                        [
                            tag,
                            taggedArticleList.has('tag')
                                ? taggedArticleList.get(tag).push(article.get('identifier'))
                                : Immutable.List().push(article.get('identifier'))
                        ]
                )
        )))

我的方法是映射传入的数据"文章",然后映射他们的"标记",并检查现有的taggedArticleList是否有密钥 - 如果它确实会在其上推送一个新的标识符,否则会创建一个新的Immutable.List并将其推送到其上。我试图劫持Immutable.Map构造函数,它接受[key, value]类型结构作为创建自身的默认方式。

返回的数据结构给了我一个奇怪的tag<List>键和一个List[tag, List[identifier]]返回形状。任何建议都非常感激。

Data Structure

1 个答案:

答案 0 :(得分:1)

我假设您的传入数据也是不可变的(您在尝试的解决方案中暗示),即

  • 整个传入数据集是一个不可变列表,
  • 每篇文章都是一个不可变的地图,
  • 每个标签系列都是不可变列表。

我的答案不包括taggedArticleList键,因为该级别的对象层次结构不会增加或消除此问题的复杂性。我假设你把它包括在内,因为它对你的特定用例很重要,但它只是这个答案的额外混乱。

请注意,在下面的代码中,reduce函数的累加器,即jsMap,是普通 JavaScript Map对象,不是不可变的Map对象,即jsMap是可变的。此解决方案需要这种可变性,并且(我相信)不违反使用不可变数据的原则,因为它只是在处理原始传入数据期间使用的中间变量,并且最终转换为Immutable地图。

如上所述,此解决方案假定您的传入数据也是不可变的。但是,如果它只是&#34;正常&#34; (可变)JavaScript对象数据,您唯一需要做的就是将article.get('tags')更改为article.tags并将article.get('identifier')更改为article.identifier

const incoming = Immutable.List([
  Immutable.Map({
    identifier: 'ADFASD',
    tags: Immutable.List([ 'tag1', 'tag2'])
  }),
  Immutable.Map({
    identifier: 'GGHYX',
    tags: Immutable.List([ 'tag2', 'tag3'])
  }),
]);

const updated = Immutable.Map(
    incoming.reduce((jsMap, article) => {
        article.get('tags').forEach(tag => {
            jsMap.set(tag, Immutable.List([
                ...(jsMap.has(tag) ? jsMap.get(tag) : []),
                article.get('identifier')
            ]));
        });
        return jsMap;
    }, new Map()) // NOTE: this is a regular JavaScript map, NOT an Immutable map
);

console.log(updated);

// console output: Map { "tag1": List [ "ADFASD" ], "tag2": List [ "ADFASD", "GGHYX" ], "tag3": List [ "GGHYX" ] }

作为旁注,请注意,在您的原始解决方案中,原始正在运行的JavaScript代码和不可用的不可变代码,我认为您错误地使用map,即使在第一种情况,你最终会得到你想要的解决方案。 map是一个函数,用于返回一个数组,其中每个元素对应于原始数组中的元素。您正在使用它来顺序获取每个元素,但之后只需使用该元素来修改其他数据,而无需从该函数返回任何内容。实际上,您想要的最终结果 not 具有映射(即对应)原始数组的数组元素。事实上,结果甚至不是一个数组,它是一个对象(或者更确切地说,一个地图......我知道,它会让人感到困惑)。我认为在这种情况下,您可能应该使用类似forEach而不是map的内容。您的代码这次工作正常,但以这种方式使用map函数可能会导致将来出现混淆/错误。你使用它的方式并不违法,而且,如你所说,可以工作。但是,并不是map打算如何使用。只是想我会就此向你提出一个单挑。