Convert this String into hashmap - java

Let's say I have an array of strings like this
String[] strings = {"game_2times",
"game_3times",
"game_4times",
"listy_bubenicek",
"listy_peneznieso"};
What I want is to create a HashMap<String, List<String>> to hold the keys and the values. The key and value of each string are separated by a "_". The resultant map for the above strings should look like the following:
"game" -> ["2times", "3times", "4times"]
"listy" -> ["bubenicek", "peneznieso"]
I tried this
for (String item: names) {
for (int i = 0; i < names.length; i++) {
ArrayList<String> c = new ArrayList<>();
if(names[i].contains("game")) {
c.add(item);
}
hashmap.put(item.split("_")[0],c);
}
}

Here is one way using streams.
String[] strings = {
"game_2times","game_3times","game_4times","listy_bubenicek",
"listy_peneznieso"
};
stream the string array using Arrays.stream
Use String.split to split each string on the _ to create an array of s[] of the two elements
use Collectors.groupingBy to group based on a key, in this case, s[0]
then use Collectors.mapping to map to s[1] and put in a list.
Map<String, List<String>> result = Arrays.stream(strings)
.map(str -> str.split("_"))
.collect(Collectors.groupingBy(s -> s[0],
Collectors.mapping(s -> s[1], Collectors.toList())));
result.entrySet().forEach(System.out::println);
prints
game=[2times, 3times, 4times]
listy=[bubenicek, peneznieso]
Here is another option sans streams.
create a map to hold the results
Iterate thru the array, splitting the string as before.
computeIfAbsent will use the supplied argument as a key if it isn't present and will create and return the value of the function, in this case, an ArrayList.
then this and subsequent references to that key will add the value to the list for that key.
Map<String, List<String>> result = new HashMap<>();
for(String str : strings) {
String[] strArray = str.split("_");
result.computeIfAbsent(strArray[0], v->new ArrayList<>()).add(strArray[1]);
}

There are more elegant ways of doing this but I will show the simple to understand way:
public static Map<String, List<String>> flatten(List<String> names) {
final Map<String, List<String>> result = new HashMap<>();
for (String item : names) {
final String[] parts = item.split("_");
if (result.containsKey(parts[0])) {
result.get(parts[0]).add(parts[1]);
} else {
final ArrayList<String> c = new ArrayList<>();
c.add(parts[1]);
result.put(parts[0], c);
}
}
return result;
}

Related

Overwriting values in a HashMap that are in an ArrayList<String>

Let's say I have a HashMap with String keys and Integer values:
map = {cat=1, kid=3, girl=3, adult=2, human=5, dog=2, boy=2}
I want to switch the keys and values by putting this information into another HashMap. I know that a HashMap cannot have duplicate keys, so I tried to put the information into a HashMap with the Integer for the keys that would map to a String ArrayList so that I could potentially have one Integer mapping to multiple Strings:
swap = {1=[cat], 2=[adult, dog, boy], 3=[kid, girl], 5=[human]}
I tried the following code:
HashMap<Integer, ArrayList<String>> swap = new HashMap<Integer, ArrayList<String>>();
for (String x : map.keySet()) {
for (int i = 0; i <= 5; i++) {
ArrayList<String> list = new ArrayList<String>();
if (i == map.get(x)) {
list.add(x);
swap.put(i, list);
}
}
}
The only difference in my code is that I didn't hard code the number 5 into my index; I have a method that finds the highest integer value in the original HashMap and used that. I know it works correctly because I get the same output even if I hard code the 5 in there, I just didn't include it to save space.
My goal here is to be able to do this 'reversal' with any set of data, otherwise I could just hard code the value. The output I get from the above code is this:
swap = {1=[cat], 2=[boy], 3=[girl], 5=[human]}
As you can see, my problem is that the value ArrayList is only keeping the last String that was put into it, instead of collecting all of them. How can I make the ArrayList store each String, rather than just the last String?
With Java 8, you can do the following:
Map<String, Integer> map = new HashMap<>();
map.put("cat", 1);
map.put("kid", 3);
map.put("girl", 3);
map.put("adult", 2);
map.put("human", 5);
map.put("dog", 2);
map.put("boy", 2);
Map<Integer, List<String>> newMap = map.keySet()
.stream()
.collect(Collectors.groupingBy(map::get));
System.out.println(newMap);
The output will be:
{1=[cat], 2=[adult, dog, boy], 3=[kid, girl], 5=[human]}
you are recreating the arrayList for every iteration and i can't figure out a way to do it with that logic, here is a good way though and without the need to check for the max integer:
for (Map.Entry<String, Integer> entry : map.entrySet()) {
String key = entry.getKey();
Integer value = entry.getValue();
List<String> get = swap.get(value);
if (get == null) {
get = new ArrayList<>();
swap.put(value, get);
}
get.add(key);
}
Best way is to iterate over the key set of the original map.
Also you have to asure that the List is present for any key in the target map:
for (Map.Entry<String,Integer> inputEntry : map.entrySet())
swap.computeIfAbsent(inputEntry.getValue(),()->new ArrayList<>()).add(inputEntry.getKey());
This is obviously not the best solution, but approaches the problem the same way you did by interchanging inner and outer loops as shown below.
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("cat", 1);
map.put("kid", 3);
map.put("girl", 3);
map.put("adult", 2);
map.put("human", 5);
map.put("dog", 2);
map.put("boy", 2);
HashMap<Integer, ArrayList<String>> swap = new HashMap<Integer, ArrayList<String>>();
for (Integer value = 0; value <= 5; value++) {
ArrayList<String> list = new ArrayList<String>();
for (String key : map.keySet()) {
if (map.get(key) == value) {
list.add(key);
}
}
if (map.containsValue(value)) {
swap.put(value, list);
}
}
Output
{1=[cat], 2=[adult, dog, boy], 3=[kid, girl], 5=[human]}
Best way I can think of is using Map.forEach method on existing map and Map.computeIfAbsent method on new map:
Map<Integer, List<String>> swap = new HashMap<>();
map.forEach((k, v) -> swap.computeIfAbsent(v, k -> new ArrayList<>()).add(k));
As a side note, you can use the diamond operator <> to create your new map (there's no need to repeat the type of the key and value when invoking the map's constructor, as the compiler will infer them).
As a second side note, it's good practice to use interface types instead of concrete types, both for generic parameter types and for actual types. This is why I've used List and Map instead of ArrayList and HashMap, respectively.
Using groupingBy like in Jacob's answer but with Map.entrySet for better performance, as suggested by Boris:
// import static java.util.stream.Collectors.*
Map<Integer, List<String>> swap = map.entrySet()
.stream()
.collect(groupingBy(Entry::getValue, mapping(Entry::getKey, toList())));
This uses two more methods of Collectors: mapping and toList.
If it wasn't for these two helper functions, the solution could look like this:
Map<Integer, List<String>> swap = map.entrySet()
.stream()
.collect(
groupingBy(
Entry::getValue,
Collector.of(
ArrayList::new,
(list, e) -> {
list.add(e.getKey());
},
(left, right) -> { // only needed for parallel streams
left.addAll(right);
return left;
}
)
)
);
Or, using toMap instead of groupingBy:
Map<Integer, List<String>> swap = map.entrySet()
.stream()
.collect(
toMap(
Entry::getValue,
(e) -> new ArrayList<>(Arrays.asList(e.getKey())),
(left, right) -> {
left.addAll(right);
return left;
}
)
);
It seams you override the values instrad of adding them to the already creared arraylist. Try this:
HashMap<Integer, ArrayList<String>> swapedMap = new HashMap<Integer, ArrayList<String>>();
for (String key : map.keySet()) {
Integer swappedKey = map.get(key);
ArrayList<String> a = swapedMap.get(swappedKey);
if (a == null) {
a = new ArrayList<String>();
swapedMap.put(swappedKey, a)
}
a.add(key);
}
I didn't have time to run it (sorry, don't have Java compiler now), but should be almost ok :)
You could use the new merge method in java-8 from Map:
Map<Integer, List<String>> newMap = new HashMap<>();
map.forEach((key, value) -> {
List<String> values = new ArrayList<>();
values.add(key);
newMap.merge(value, values, (left, right) -> {
left.addAll(right);
return left;
});
});

Counting same Strings from Array in Java

How can I count the same Strings from an array and write them out in the console?
The order of the items should correspond to the order of the first appearance of the item. If there are are two or more items of a kind, add an "s" to the item name.
String[] array = {"Apple","Banana","Apple","Peanut","Banana","Orange","Apple","Peanut"};
Output:
3 Apples
2 Bananas
2 Peanuts
1 Orange
I tried this:
String[] input = new String[1000];
Scanner sIn = new Scanner(System.in);
int counter =0;
String inputString = "start";
while(inputString.equals("stop")==false){
inputString = sIn.nextLine();
input[counter]=inputString;
counter++;
}
List<String> asList = Arrays.asList(input);
Map<String, Integer> map = new HashMap<String, Integer>();
for (String s : input) {
map.put(s, Collections.frequency(asList, s));
}
System.out.println(map);
But I don't know how to get the elements out of the Map and sort them like I would like.
You can use a Map to put your result, here is a simple example:
public static void main(String args[]){
String[] array = {"Apple","Banana","Apple","Peanut","Banana","Orange","Apple","Peanut"};
Map<String, Integer> result = new HashMap<>();
for(String s : array){
if(result.containsKey(s)){
//if the map contain this key then just increment your count
result.put(s, result.get(s)+1);
}else{
//else just create a new node with 1
result.put(s, 1);
}
}
System.out.println(result);
}
Use Java streams groupingBy and collect the results into a Map<String, Long> as shown below:
String[] array = {"Apple","Banana","Apple","Peanut","Banana","Orange","Apple", "Peanut"};
Map<String, Long> map = Stream.of(array).collect(Collectors.
groupingBy(Function.identity(), //use groupingBy array element
Collectors.counting())); //count number of occurances
System.out.println(map);//output the results of the Map
Java 8 would allow a pretty elegant way of doing this with groupingBy and counting. Using a LinkedHashMap instead of the default map should handle the ordering:
Arrays.stream(array)
.collect(Collectors.groupingBy(Function.identity(),
LinkedHashMap::new,
Collectors.counting()))
.entrySet()
.forEach(e -> System.out.println(e.getValue() +
"\t" +
e.getKey() +
(e.getValue() > 1 ? "s" : "")));
use java 8
Map<String, Long> myMap = Stream.of(array).collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));

How to GROUP BY same strings in Java

I have got an Arraylist of strings and I need to return the Arraylist indexes of strings that are the same.
For example
Arraylist[0]: IPAddress
Arraylist[1]: DomainName
Arraylist[2]: IPAddress
Arraylist[3]: Filesize
The output should be:
Arraylist[0]
IPAddress|0,2 //0,2 denotes the arraylist index that is of the same
Arraylist[1]
DomainName|1
Arraylist[2]
Filesize|3
Any idea how can this be achieved?
What I have done is:
for(int i=0; i<arr.size(); i++){
if(arr.get(i).equals(arr.size()-1)){
//print index
}
}
With Java8 streams
List<String> strings = Arrays.asList("IPAddress", "DomainName", "IPAddress", "Filesize");
Map<String, List<Integer>> map = IntStream.range(0, strings.size()).boxed().collect(Collectors.groupingBy(strings::get));
System.out.println(map);
output
{DomainName=[1], Filesize=[3], IPAddress=[0, 2]}
To get the results in ordered
Map<String, List<Integer>> map = IntStream.range(0, strings.size())
.boxed()
.collect(Collectors.groupingBy(strings::get, LinkedHashMap::new, Collectors.toList()));
The mechanical steps are fairly straightforward:
Get a collection which can support a key (which is the string in your list) and a list of values representing the indexes in which they occur (which would be another ArrayList).
If the element exists in the collection, simply add the index to its value.
Otherwise, create a new list, add the index to that, then add that to the collection.
Here is some sample code below.
final List<String> list = new ArrayList<String>() {{
add("IPAddress");
add("DomainName");
add("IPAddress");
add("Filesize");
}};
final Map<String, List<Integer>> correlations = new LinkedHashMap<>();
for (int i = 0; i < list.size(); i++) {
final String key = list.get(i);
if (correlations.containsKey(key)) {
correlations.get(key).add(i);
} else {
final List<Integer> indexList = new ArrayList<>();
indexList.add(i);
correlations.put(key, indexList);
}
}
Any optimizations to the above are left as an exercise for the reader.

How to build an object from similar key value prefix in a map

Firstly I am using Java to write this.
So I have a Map with keys and values like this.
Key = Value
"a.1" = "a1"
"a.2" = "a2"
"a.3" = "a3"
"b.1" = "b1"
"b.2" = "b2"
"b.3" = "b3"
"c.1" = "c1"
"c.2" = "c2"
"c.3" = "c3"
What I need to do is get it to split all up and make so that eventually I can loop through and create new objects using
someloop{
new someObject(a1,b1,c1); // new someObject(a2,b2,c2); // new someObject(a3,b3,c3);
}
I need to be able to make it dynamic so I can add another prefix (d,e) and also check if a number is missing or is skipped.
If you change the constructor of SomeObject to accept a list of Strings, this might work:
Map<String, String> map = new HashMap<>();
map.put("a.1", "a1");
map.put("a.2", "a2");
map.put("a.3", "a3");
map.put("b.1", "b1");
map.put("b.2", "b2");
map.put("b.3", "b3");
map.put("c.1", "c1");
map.put("c.2", "c2");
map.put("c.3", "c3");
Map<String, List<String>> grouped = map.entrySet().stream()
.sorted(Comparator.comparing(Map.Entry::getKey))
.collect(Collectors.groupingBy(
entry -> entry.getKey().split("\\.")[0],
HashMap::new,
Collectors.mapping(Map.Entry::getValue, Collectors.toList())));
List<SomeObject> objects = grouped.values().stream().map(SomeObject::new).collect(Collectors.toList());
System.out.println(objects);
I have omitted input validation:
public static void buildObjects(Map<String, String> keyValuePairs) {
List<List<String>> sortedValues = new ArrayList<>();
// assuming keys are ending in a digit 1 through 9, add empty lists to sortedValues to hold values
sortedValues.add(null); // index 0
for (int index = 0; index <= 9; index++) {
sortedValues.add(new ArrayList<>());
}
for (Map.Entry<String, String> pair : keyValuePairs.entrySet()) {
String key = pair.getKey();
int indexOfDot = key.indexOf('.');
int suffix = Integer.parseInt(key.substring(indexOfDot + 1));
sortedValues.get(suffix).add(pair.getValue());
}
for (List<String> list : sortedValues) {
if (list != null && ! list.isEmpty()) {
new SomeObject(list.toArray(new String[list.size()]));
}
}
}
You will probably also want to add code that does something with the created objects.
If you want to be sure about the order of values passed to the constructor, you may use a new TreeMap(keyValuePairs) or even new TreeMap(yourComparator).putAll(keyValuePairs). This will control the order in which the keys are processed.

java8 convert string array to map(odd index is key, even index is value)

Now I have a String array,
String[] a= {"from","a#a.com","to","b#b.com","subject","hello b"};
from command line arguments.
I want to convert it to Map,
{"from":"a#a.com","to":"b#b.com","subject":"hello b"}
Does exist convenient manner in java8 to achieve this?
Now my way is
Map<String,String> map = new HashMap<>();
for (int i = 0; i < args.length; i+=2) {
String key = args[i].replaceFirst("-+", ""); //-from --> from
map.put(key, args[i+1]);
}
You can use an IntStream to iterate on the indices of the array (which is required in order to process two elements of the array each time) and use the Collectors.toMap collector.
The IntStream will contain a corresponding index for each pair of elements of the input array. If the length of the array is odd, the last element will be ignored.
Map<String,String> map =
IntStream.range(0,a.length/2)
.boxed()
.collect(Collectors.toMap(i->a[2*i].replaceFirst("-+", ""),
i->a[2*i+1]));
You can do this quite elegant with Javaslang:
String[] a= {"from","a#a.com","to","b#b.com","subject","hello b"};
Map<String, String> map = Stream.of(a).grouped(2) // use javaslang.collection.Stream here
.map(group -> group.toJavaArray(String.class))
.toJavaStream() // this is the plain old java.util.Stream
.collect(toMap(tuple -> tuple[0], tuple -> tuple[1]));
The grouped function groups your stream in groups of 2 elements. These can be transformed to string arrays and those can be the base of a Map. Probably Javaslang allows you to do even more elegant.
In Java 8 you can use Stream.iterate to divide list to sublists of 2 elements
String[] a= {"from","a#a.com","to","b#b.com","subject","hello b"};
Map<String, String> map = Stream.iterate(
Arrays.asList(a), l -> l.subList(2, l.size()))
.limit(a.length / 2)
.collect(Collectors.toMap(
l -> l.get(0).replaceFirst("-+", ""),
l -> l.get(1))
);
Another recursive solution using simple Iterator
Map<String, String> map = buildMap(new HashMap<>(), Arrays.asList(a).iterator());
private static Map<String, String> buildMap(
Map<String, String> map, Iterator<String> iterator) {
if (iterator.hasNext()) {
map.put(iterator.next().replaceFirst("-+", ""), iterator.next());
createMap(map, iterator);
}
return map;
}
The trick is to first join the string, then split it up into key/value pairs, then the task is simple:
Map<String,String> map = Arrays.stream(
String.join(",", a)
.split(",(?!(([^,]*,){2})*[^,]*$)"))
.map(s -> s.split(","))
.collect(Collectors.toMap(s -> s[0], s -> s[1]))
;

Categories