23

Suppose I have an immutable.js List like this:

var xs = Immutable.fromJS([{key: "k", text: "foo"}])

I want to transform that into a Map that looks like this:

var ys = Immutable.fromJS({k: {key: "k", text: "foo"}})

How do I turn xs into ys idiomatically in immutable.js?

By idiomatically I mean that any intermediate structures should be immutable.js structures, and I think that any intermediate steps would use iterable composition instead of creating full-size intermediate representations.

Edit: I've created a jsperf benchmark comparing the answers provided so far. Both non-mutating solutions feel fairly idiomatic to me; I may hold out and let votes decide the chosen answer or hold out until we can gather a collection of roughly equally idiomatic techniques.

Community
  • 1
  • 1
CJ Gaconnet
  • 1,421
  • 1
  • 12
  • 18

4 Answers4

23

You can reduce the input list into new Immutable.Map:

var ys = xs.reduce(
      function(result, item) { return result.set(item.get('key'), item); }, 
      Immutable.Map());

There is no intermediate representation in this case, but the immutable map has to be updated (in fact, new instance must be created) in every iteration and that that can be suboptimal (depending on the actual implementation of Immutable.Map).

You can optimize it reducing into regular javascript object and convert it to immutable map at the end:

var ys = Immutable.Map(xs.reduce(
      function(result, item) { result[item.get('key')] = item; return result; }, 
      {}));

But that is certainly less idiomatic.

dkl
  • 3,850
  • 2
  • 27
  • 26
  • Thanks for the well-reasoned response! I think this is the best answer. But when I compare the performance of updating the Map at each iteration vs constructing a new one at the end, I'm consistently seeing that doing it the first way is almost 10% faster, which is the opposite of what you mentioned at the end: https://jsperf.com/immutable-js-list-of-maps-to-map-of-maps/1 – theopak Apr 13 '17 at 21:24
23

One solution may be to use Map constructor from List:

const ys = Immutable.Map(xs.map(v => [v.get('key'), v]));
Julien Deniau
  • 4,880
  • 1
  • 18
  • 22
2

Seems like this, but i'm not sure about intermediate representations

ys = Immutable
    .fromJS([{key:'k', value:'xxx'}])
    .toKeyedSeq()
    .mapEntries(function (kv) { return [kv[1].get('key'), kv[1]]; })

PS It's strange, but i'm can only use .toKeyedSeq, not .toKeyedIterable, which is more appropriate.

mbeloshitsky
  • 327
  • 1
  • 6
0

You can swap a List of Maps to a Map of Lists by using reduce combined with mergeWith.

Reduce through the collection and merge each new item by concatenating its values together.

collection.reduce((rr, ii) => rr.mergeWith((aa, bb) => List().concat(aa, bb), ii))
Allan Hortle
  • 2,435
  • 2
  • 20
  • 22