Closed GoogleCodeExporter closed 9 years ago
This comes up occasionally, but has never been very compelling. Can you provide
motivation? What works is:
(a) A scenario that feels pretty common
(b) Some ugly code to handle that scenario today
(c) What the code would look like if Maps.fromFunction() existed.
Original comment by kevin...@gmail.com
on 27 May 2008 at 7:02
I keep writing the following method in many projects:
public static <K, V> Map<K, V> hashMapFromFunction(Set<K> keys, Function<K, V>
function) {
Map<K, V> map = Maps.newHashMapWithExpectedSize(keys.size());
for (K key : keys) {
map.put(key, function.apply(key));
}
return map;
}
A common scenario would be to cache the result of applying the function to each
element, for example to later sort using the (cached values of the) function.
Nothing really ugly here, but one could for example forget to set the initial
size of
the map.
Original comment by sebd...@gmail.com
on 25 Aug 2008 at 2:26
I just came up with the same/similar idea and found this item. This is how I
would
imagine this feature.
Function<String, String> functionToLower = new Function<String, String>() {
Sring apply (String input) {
return input.toLower();
}
}
Map<String, Object> testMap =
Maps.createHashMapWithKeyFunction(functionToLower);
testMap.put("AKEY", someObject);
asssertTrue(testMap.contains("akey"));
assertEquals(someObject, testMap.get("akey");
The function basically enables you to map your keys to a different Object which
in
turn means you can use custom equals and hashcode implementations just for that
map.
Is there any other way to do this?
Maybe this could also be implemented as part of the map maker.
Original comment by Florian....@gmail.com
on 2 Apr 2009 at 9:10
This would be extremely counter-intuitive for a map where you both write and
read.
(But for a write-only scenario it seems ok).
testMap.put("AKEY", someObject);
assertTrue(testMap.containsKey("AKEY")); // ??
In any case, can't you use a ForwardingMap to transform the keys before you
apply
them to the delegate map
---
Another similar proposal (I don't think it's too compelling though):
Given a bijection A<-->B (i.e. both Function<A, B> and Function <B, A>), create
from
a Map<A, V> a map view of type Map<B, V>, and have Maps#transformKeys along
with
Map#transformValues.
(The Function<A, B> is only needed for implementing Map#keySet of the view if I
recall correctly, if it weren't for that, a Function<B, A> would probably
suffice).
Original comment by jim.andreou
on 2 Apr 2009 at 2:41
My proposal was meant as a custom hash function which is applied while storing
values
AND getting values from the Map. In the example a value stored with a lower
case key
could also be retrieved with an upper case key.
I'll look into extending a ForwardingMap to provide this functionality and I
realized
it's a bit different from what was requested here initially.
Meanwhile, I found that Maps.uniqueIndex() and MultiMaps.index() work well
enough for
my usecases.
Original comment by Florian....@gmail.com
on 9 Apr 2009 at 6:23
i just wrote this trivial implementation, since i have a lot of code like this
in my
Enums:
static final Map<String, CountryEnum> reverseLookup;
static {
HashMap<String, CountryEnum> ret = new HashMap<String, CountryEnum>();
for (CountryEnum oneCountry : CountryEnum.values()) {
ret.put(oneCountry.right, oneCountry);
}
reverseLookup = ret;
}
public static <K,V> ImmutableMap<K,V> mapValues(V[] data , Function<V,K>
keyGetter){
return mapValues(Arrays.asList(data),keyGetter);
}
public static <K,V> ImmutableMap<K,V> mapValues(Iterable<V> data , Function<V,K>
keyGetter){
ImmutableMap.Builder<K,V> b = ImmutableMap.builder();
for (V value : data) {
b.put(keyGetter.apply(value),value);
}
return b.build();
}
this reduces the code to :
static final Map<String, CountryEnum> reverseLookup = CommonUtils.mapValues(
CountryEnum.values(),new Function<CountryEnum, String>() {
public String apply(CountryEnum from) {
return from.right;
}
});
this is used a lot when the same enum is represented in the database by
different
types of keys (sometimes ISO country code, sometimes "internal" code sometimes
id..)
this flavor produces a ImmutableMap, which is what i need in most cases.
but there could be a lot of other possibilities:
lazy mapping - only evaluate the given function when it is needed.
you will not need to evaluate it when just iterating the values.
you will not need to evaluate all of them it when you say
mappedMap.get(someValue)
and the first element of the iterator would already return "someValue".
in case of such a lazy initialization, one would need to maintain a partially
filled
hashmap. the actual lookup of values would be split in two parts: 1) lookup in
intermediate map if not found 2) iterate until key is found. while iterating
fill
intermediate map.
3) if iterator is at its end, the map may become a immutableMap?
semantics get complicated if you try to insert in such a
iterable+function-backed
map. even more so, if it is yet only partially evaluated.
Original comment by heroldsi...@googlemail.com
on 13 May 2009 at 12:21
Wow... V[]? Shouldn't that beast be abolished? Prefer List<V>.
Original comment by jim.andreou
on 13 May 2009 at 12:30
V[] is an overloading, to use .mapValues(..) together with Enums, which provide
the
.values() method (which provide only an array). List<V> would already be
covered by
Iterable<V>
concise is nice.
Original comment by heroldsi...@googlemail.com
on 13 May 2009 at 12:49
I agree concise is nice, but small api surface is also nice; if this was
applied
consistently across google collections, it would easily end up doubling many of
its
methods... (Sorry, I commented without grokking the full code, just skimmed
through
it).
Original comment by jim.andreou
on 13 May 2009 at 1:19
[deleted comment]
i agree, not having arrays is consistent with the google-maps api
instead of CountryEnum.values() which produces an array i could as well use
EnumSet.allOf(CountryEnum.class) which is Iterable. - so V[] is not really
needed.
on second thought the keyGetter argument does not strictly need to be typed
with V, ?
super V is sufficient.
on a related note, i have overlooked Maps.uniqueIndex this does already exactly
what
i need. i feel bad :(
Original comment by heroldsi...@googlemail.com
on 13 May 2009 at 3:35
Original comment by kevin...@gmail.com
on 17 Sep 2009 at 5:57
Original comment by kevin...@gmail.com
on 17 Sep 2009 at 6:02
I often find myself creating a maps where the key is based on the value, rather
than
the other way around. So the form
Map<K, V> fromFunction(Set<V> values, Function<V, K> function);
would also be appreciated.
Original comment by davemi...@gmail.com
on 21 Oct 2009 at 9:41
@daveminor
What would that fromFunction method do if the provided Function was not
one-to-one?
(Do you want a Multimap instead?)
Original comment by cresw...@gmail.com
on 21 Oct 2009 at 9:51
We already have that functionality in Maps.uniqueIndex(), or Multimaps.index()
for
the case creswick mentioned.
Original comment by jared.l....@gmail.com
on 21 Oct 2009 at 9:57
Ah, thank you - overlooked it because of the name.
Original comment by gmtetest@gmail.com
on 21 Oct 2009 at 11:59
This issue has been moved to the Guava project (keeping the same id number).
Simply replace 'google-collections' with 'guava-libraries' in your address
bar and it should take you there.
Original comment by kevinb@google.com
on 5 Jan 2010 at 11:09
Original issue reported on code.google.com by
sebd...@gmail.com
on 2 Apr 2008 at 11:03