Stefan Winkler
Stefan Winkler

Reputation: 3956

Create an immutable map from a collection of pairs in Xtend

I have a collection of objects O from which I want to create an (immutable) map of two features (say, O.name and O.value).

My best solution so far is

newHashMap(o.map[it.name -> it.value])

But this will instantiate and initialize a new HashMap which is not acually what I want. Instead I want an immutable map without unneeded instantiation, similarly what

o.toInvertedMap[value]

returns - but this maps O to O.value.

Is there a method in the Xtend library to achieve what I want?

Note: to add some context to my general question above, I actually want to get a map of attribute names and values for my EMF EObjects:

newHashMap(eObject.eClass.EAttributes.map[it.name -> eObject.eGet(it).toString])

Edit:

I just found this:

newImmutableMap(o.map[it.name -> it.value])

This seems more like what I want. Is this the "best" way to write it?

Upvotes: 3

Views: 897

Answers (3)

raner
raner

Reputation: 1295

The "best" way of writing this conversion can mean a lot of different things.

Probably the most straightforward way to convert a collection of Pairs to a regular mutable (!) map is to simply use

collection.toMap([key], [value])

(I'm not sure I fully understand the comment on @Jon's answer: "there is no extension method toMap or toImmutableMap for this. (there is toMap(computeKeysFunction), but this is not usable to convert a list of pairs to a map" – in addition to toMap(computeKeysFunction) the IterableExtensions also define a toMap(computeKeysFunction, computeValuesFunction), which can flexibly convert lists into maps, including lists of pairs.)

Now, as mentioned above, the toMap extension method will produce a mutable map, so if you absolutely need an immutable map you have to apply an extra method, depending on what specifically you mean by "immutable map". To create what the Java Collections API calls an "unmodifiable" map, you can simply use the Map.copyOf method. If you explicitly need a Guava ImmutableMap (which is what CollectionLiterals.newImmutableMap will return, though aliased as a regular Map) you would have to use ImmutableMap.copyOf instead:

def <K, V> Map<K, V> collectionOfPairsToRegularMap(Collection<Pair<K, V>> collection)
{
  collection.toMap([key], [value])
}

def <K, V> Map<K, V> collectionOfPairsToUnmodifiableMap(Collection<Pair<K, V>> collection)
{
  Map.copyOf(collection.toMap([key], [value]))
}

def <K, V> ImmutableMap<K, V> collectionOfPairsToImmutableMap(Collection<Pair<K, V>> collection)
{
  ImmutableMap.copyOf(collection.toMap([key], [value]))
}

def <K, V> Map<K, V> collectionOfPairsToAliasedImmutableMap(Collection<Pair<K, V>> collection)
{
  newImmutableMap(collection)
}

(If saving key strokes is a goal, you can further shorten some of the code by calling .copyOf as an extension method.)

That all being said, there are obvious performance differences between these approaches, and some approaches will create multiple temporary maps.

On the other hand, using a Collection<Pair<K, V>> as an intermediate may not be necessary at all (unless the data already arrives in that form). For the EAttribute example in the question one could simply use toMap directly:

def Map<String, String> attributes(EObject eObject)
{
  eObject.eClass.EAttributes.toMap([name], [eObject.eGet(it).toString])
}

Again, if absolutely needed, this requires additional code for producing an immutable map, so the overall effort in that case would probably be similar to first converting the list of EAttributes to a list of Pairs and then using newImmutableMap.

Upvotes: 0

Stefan Winkler
Stefan Winkler

Reputation: 3956

The best way I have found so far is (as in the Update above):

newImmutableMap(o.map[name->value])

Upvotes: 2

Jon
Jon

Reputation: 398

o.map[name->value].toImmutableMap?

Upvotes: 0

Related Questions