Reputation: 770
I am trying to update an Immutable.Map
based on some incoming, paged, data, building a top-level index in my data structure.
The incoming data looks like this:
[
{
identifier: ADFASD,
tags: [ tag1, tag2]
}, {
identifier: GGHYX,
tags: [ tag2, tag3]
},
]
Desired Result
{
taggedArticleList: {
tag1: [ADFASD],
tag2: [ADFASD, GGHYX],
tag3: [GGHYX],
}
}
The current JS function that I'm refactoring out (that works):
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
And my current ImmutableJS attempt:
.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'))
]
)
)))
My approach is to map through the incoming data "articles", and then map through their "tags", and check if the existing taggedArticleList
has the key - if it does push a new identifier onto it, otherwise create a new Immutable.List
and push it on then. I'm trying to hijack the Immutable.Map
constructor, that accepts a [key, value]
type structure as a default way to create itself.
The data structure being returned is giving me a weird tag<List>
key, and a List[tag, List[identifier]]
return shape. Any advice much appreciated.
Upvotes: 0
Views: 425
Reputation: 12458
I am assuming that your incoming data is also immutable (which you imply in your attempted solution), i.e.
My answer does not include the taggedArticleList
key, as that level of object hierarchy doesn't add to or take away from the complexity of this question. I assume you included it because it is important for your particular use case, but it's only extra clutter for this answer.
Note that in the code below, the accumulator for the reduce
function, i.e. jsMap
, is a normal JavaScript Map object, not an Immutable Map object, i.e. jsMap
is mutable. This mutability is required for this solution to work, and (I believe) does not violate principles of working with immutable data because it is simply an intermediate variable used during processing of the original incoming data and is, in the end, converted to an Immutable Map.
As stated above, this solution assumes that your incoming data is also Immutable. However, if it was just "normal" (mutable) JavaScript object data, the only thing you would need to do is change article.get('tags')
to article.tags
and change article.get('identifier')
to 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" ] }
Just as a side note, also note that in your original solutions, both the original working JavaScript code and the non-working Immutable code, I think you're using map
incorrectly, even though, in the first case, you eventually get your desired solution. map
is a function that is intended to return an array in which each element corresponds to an element in the original array. You are using it to obtain each element sequentially, but then simply use that element to modify other data without returning anything from that function. In fact, the eventual result that you want does not have array elements that map (i.e. correspond) to the original array. In fact, the result isn't even an array, it's an object (or, more precisely, a Map...I know, it gets confusing). I think that in cases like this, you probably should be using something like forEach
instead of map
. Your code worked this time, but using the map
function in this way will possibly lead to confusion/bugs in the future. The way you used it isn't illegal, and, as you demonstrated, can work. However, it's not how map
was intended to be used. Just thought I'd give you a heads-up about that.
Upvotes: 1