Average counts from HashMap using java 8 stream API? - dictionary

I have a Map of the following type
public class MapUtils {
private Map<String, Integer> queryCounts = new HashMap<>();
public void averageCounters(){
int totalCounts = queryCounts.values().stream().reduce(0, Integer::sum);
queryCounts = queryCounts.entrySet()
.stream()
.collect(Collectors.toMap(
Map.Entry::getKey,
(Map.Entry::getValue)/totalCounts
));
}
This does not compile and shows error in this line (Map.Entry::getValue)/totalCounts. How do I fix this? Is there a better way to get achieve average over Map using Java 8 API?
EDIT:
Is this a better approach?
queryCounts.entrySet()
.forEach(entry -> queryCounts.put(entry.getKey(),
entry.getValue()/totalCounts));

If you want in-place modification, it's much better to use Map.replaceAll instead of Stream API:
int totalCounts = queryCounts.values().stream()
.collect(Collectors.summingInt(Integer::intValue));
queryCounts.replaceAll((k, v) -> v/totalCounts);
However in your case this solution is problematic as division results will be rounded to an int number, thus you almost always will got zeroes in the result. Actually there's the same problem in your code. You probably want to have Map<String, Double> as the resulting type. So you probably need to create a completely new Map:
Map<String, Double> averages = queryCounts.entrySet().stream()
.collect(Collectors.toMap(Entry::getKey,
e -> ((double)e.getValue())/totalCounts));
An alternative would be to have queryCounts declared as Map<String, Double> in the first place. This way you can use replaceAll:
double totalCounts = queryCounts.values().stream()
.collect(Collectors.summingDouble(Double::doubleValue));
queryCounts.replaceAll((k, v) -> v/totalCounts);
Finally there's also one more alternative which is the most efficient, but dirty. Your code assumes that original (non-averaged) queryCounts are unnecessary after averageCounters() is called. Thus you can keep queryCounts as Map<String, Integer> (which is more effective than counting to Map<String, Double>), but then change the Map values type like this:
double totalCounts = queryCounts.values().stream()
.collect(Collectors.summingInt(Integer::intValue));
Map<String, Object> map = (Map<String, Object>)queryCounts;
map.replaceAll((k, v) -> ((Integer)v)/totalCounts);
Map<String, Double> averages = (Map<String, Double>)map;
queryCounts = null;
The similar trick is performed in JDK inside the Collectors.groupingBy implementation.

Related

Java8 Create Map grouping by key contained in values

I have the following two lists of String:
{APPLE, ORANGE, BANANA} //call it keyList
{APPLE123, ORANGEXXX, 1APPLE, APPLEEEE} //call it valueList
Desired output is an HashMap<String, List<String>> like this:
<APPLE, {APPLE123, 1APPLE, APPLEEEE}>
<ORANGE, {ORANGEXXX}>
<BANANA, {}> //also <key, null> is accepted
I have implemented this solution(it works)
HashMap<String, List<String>> myMap = new HashMap<>();
keyList.forEach(key -> {
List<String> values = valueList.stream()
.filter(value -> value.contains(key))
.collect(Collectors.toList());
myMap.put(key, values);
});
Given the assumption that a value is related to only one key (it's a constraint of my domain), is this the best solution in java8 , in terms of performance and/or code cleaning ?
Can it be tuned in some way?
If you can safely assume that each value is associated with a key, and only one key, you can go into the following direction:
Pattern p = Pattern.compile(String.join("|", keyList));
Map<String, List<String>> map = valueList.stream()
.collect(Collectors.groupingBy(s -> {
Matcher m = p.matcher(s);
if(!m.find()) throw new AssertionError();
return m.group();
}));
map.forEach((k,v) -> System.out.println(k+": "+v));
If the keys may contain special characters which could get misinterpreted as regex constructs, you can change the preparation code to
Pattern p = Pattern.compile(
keyList.stream().map(Pattern::quote).collect(Collectors.joining("|")));
The collect operation does only create the groups for existing values. If you really need all keys to be present, you can use
Map<String, List<String>> map = valueList.stream()
.collect(Collectors.groupingBy(s -> {
Matcher m = p.matcher(s);
if(!m.find()) throw new AssertionError();
return m.group();
},
HashMap::new, // ensure mutable map
Collectors.toList()
));
keyList.forEach(key -> map.putIfAbsent(key, Collections.emptyList()));
or
Pattern p = Pattern.compile(
keyList.stream().map(Pattern::quote)
.collect(Collectors.joining("|", ".*(", ").*")));
Map<String, List<String>> map = valueList.stream()
.map(p::matcher)
.filter(Matcher::matches)
.collect(Collectors.groupingBy(m -> m.group(1),
HashMap::new, // ensure mutable map
Collectors.mapping(Matcher::group, Collectors.toList())
));
keyList.forEach(key -> map.putIfAbsent(key, Collections.emptyList()));

Extract keys and left values from a Map<?, Either> in Javaslang

Given a Map<String, Either<Boolean, Integer>, what's the most straightforward way to convert it to a Map<String, Boolean> containing only the entries with boolean values?
Right now I have this:
Map<String, Boolean> boolMap = eitherMap
.filter(entry -> entry._2.isLeft())
.map((key, value) -> Tuple.of(key, value.getLeft())
;
This works, but seems unnecessarily wordy, and it seems like there should be some tighter, one-step, “flatmap that 💩” solution.
Disclaimer: I'm the creator of Javaslang
Here is a solution based on javaslang-2.1.0-alpha. We take advantage of the fact that Either is right-biased. Because we focus on the left values, we swap the Either. The flatMap iterates then over the Either, if it contains a boolean.
import javaslang.collection.Map;
import javaslang.control.Either;
import static javaslang.API.*;
public class Test {
public static void main(String[] args) {
Map<String, Either<Boolean, Integer>> map = Map(
"a", Left(true),
"b", Right(1),
"c", Left(false));
Map<String, Boolean> result =
map.flatMap((s, e) -> e.swap().map(b -> Tuple(s, b)));
// = HashMap((a, true), (c, false))
println(result);
}
}
You achieve the same in javaslang-2.0.5 by using static factory methods like Tuple.of(s, b).

Create list of map using streams and lambda expressions

for (String varValue : arrayList1) {
Map<String, String> mapInstance = new HashMap<>();
val.put(KEY, VALUE);
val.put(VAR_KEY, varValue);
arrayList2.add(mapInstance);
}
Basically, I want to create a map with two entries and then add each of these maps to a list.
Final list:
{KEY,VALUE} {VAR_KEY,arrayList1.get(0)}
{KEY,VALUE} {VAR_KEY,arrayList1.get(1)}
{KEY,VALUE} {VAR_KEY,arrayList1.get(2)}
...
and so on
It seems you only need a simple map stage.
List<Map<String, String>> list = arrayList1.stream().map(t -> {
Map<String, String> map = new HashMap<>();
map.put("KEY", "VALUE");
map.put("VAR_KEY", t);
return map;
}).collect(Collectors.toList());
What is KEY and VAR_KEY? are they instance variable of some object which you are trying to put in Map from the incoming object.
However, you can try something like this :
Map result =
arrayList1.stream().collect(Collectors.toMap(Class::getKey, c -> c));

transform Map<Integer, Double> to Iterable<Tuple2<Integer, Double>>

I'm stuck with data transformations in Java-8...
Somebody knows how to transform Map<Integer, Double> to Iterable<Tuple2<Integer, Double>>, or even
Map.Entry<Integer, Double> to Tuple2<Integer, Double>?
Thank you!
A Map<> has a Set<Map.Entry<K, V>> entrySet() method. This gives you a Set of entries you can iterate over.
If you want to iterate over Tuple2<Integer, Double> objects, you have to convert the Entry objects. To do so, you can e. g. transform your Set into a Stream:
Step by step:
Stream<Entry<Integer, Double>> myEntryStream = myMap.entrySet().stream();
Stream<Tuple2<Integer, Double>> myTuple2Stream = myEntryStream.map(entry -> new Tuple2(entry.getKey(), entry.getValue()));
or simply
Stream<Tuple2<Integer, Double>> myStream = myMap.entrySet().stream().map(entry -> new Tuple2(entry.getKey(), entry.getValue()));
To iterate over this, you can do
Iterable<Tuple2<Integer, Double>> it = myStream::iterator;
or just
for (Tuple2<Integer, Double> item: (Iterable<Tuple2<Integer, Double>>) myStream::iterator) {
// do sth with item
}
If your Tuple2, whatever it is, has a constructor which takes a Map.Entry, you can even do
.map(Tuple2::new)
in order to achieve your goal.

TinkerPop Frames - storing a Map<String,String> into a vertex properties

I'd like to persist a bare Map to vertex properties.
The motivation is that I don't know in advance which properties the map will contain.
And storing one vertex per property doesn't seem effective.
How would I do that?
interface Foo {
#Properties...?
Map<String,String> getProperties();
#Properties
Map<String,String> addProperty();
}
Perhaps through method handlers. How?
And is there any native support?
I've added support for it using the handlers.
See the Windup project.
https://github.com/windup/windup/pull/157
This is how it looks in models.
This one stores the map in the props of given frame's vertex, using a prefix map:
#TypeValue("MapInAdjPropsModelMain")
public interface MapMainModel extends WindupVertexFrame
{
#InProperties(propPrefix = "map") void setMap(Map<String, String> map);
#InProperties(propPrefix = "map") Map<String, String> getMap();
}
And this one stores the map in an adjacent vertex, hence can store multiple maps:
#TypeValue("MapInAdjPropsModelMain")
public interface MapMainModel extends WindupVertexFrame
{
#InAdjacentProperties(edgeLabel = "map")
void setMap(Map<String, String> map);
#InAdjacentProperties(edgeLabel = "map")
Map<String, String> getMap();
#InAdjacentProperties(edgeLabel = "map2")
void setMap2(Map<String, String> map);
#InAdjacentProperties(edgeLabel = "map2")
Map<String, String> getMap2();
}

Resources