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()));
Related
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;
}
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;
});
});
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]))
;
I have finish my code to find top 20 words after search many times , but it is not in descending word.
I need to add the code to Sort the list by frequency in a descending order, if two words have the same number account:
{ 'cat' => 43, 'c' => 43 }
the output should be
c
cat
My code is :
public static void main(String[] args) throws IOException{
String delimiters = ".;_?>*/";
String[] result = new String[20];
List<String> listArray = new ArrayList<String>();
Map<String, Integer> map = new HashMap<String, Integer>();
FileReader fileR = new FileReader("D:/test.txt");
BufferedReader bufferedR = new BufferedReader(in);
String line;
while ((line = bufferedR.readLine()) != null) {
StringTokenizer sToken = new StringTokenizer(line, delimiters);
while (sToken.hasMoreTokens()) {
String token = sToken.nextToken().trim().toLowerCase();
if (map.containsKey(token)) {
int val = map.get(token);
val++;
map.put(token, val);
} else{
map.put(token, 1);
}
}
}
bufferedR.close();
for(int i=0;i<result.length;i++){
int mValu=0;
String wKey="";
for(Map.Entry<String,Integer> entry:map.entrySet()){
if(entry.getValue()>mValu){
mValue=entry.getValue();
wKey=entry.getKey();
}
}
map.remove(wKey);
result[i]=wKey;
}
for (int i = 0 ; i<result.length;i++){
System.out.println(result[i]);
}
}
}
When i research about this subject i found this code but don't know how fit it into my code:
List<Map.Entry<String, Integer>> entries = new `ArrayList`<Map.Entry<String, Integer>>(map.entrySet());
Collections.sort(entries, new Comparator<Map.Entry<String, Integer>>() {
public int compare(Map.Entry<String, Integer> a, Map.Entry<String, Integer> b) {
return Integer.compare(b.getValue(), a.getValue());
}
});
Or have a better idea how to get frequency in a descending order?!
Thanks for help.
You can do:
import java.util.Comparator.*;
import java.util.stream.Collectors.*;
Map<String, Integer> map = // ...
List<String> ss = map.entrySet().stream()
.sorted(comparing(e -> e.getValue())
.reversed()
.thenComparing(e -> e.getKey()))
.map(e -> e.getKey())
.collect(toList());
What you need to do is sort your map. From what I understand is that you want the top 20 values, and if two values have the same token, then it should be in lexicographical order.
My solution would be to sort your map first by key (token in your case) and then sort by values.
This way the sorted order of the tokens will stay intact, and the map order will be in the way you want the output to be in.
caution
Make sure that the sorting algorithm you use is an in place sorting algo like Quicksort, otherwise the above solution won't work.
I want to retrieve k,v-pairs from a HashMap.
The entrys are like this:
a = 3,4
b = 5,6
and so on. I need combinations of these values.
a=3, b=5
a=3, b=6
a=4, b=5
a=4, b=6
I don't know how many keys and how many entrys the values have. With entrySet I can get the values but not combinations. It looks like recursion but how?
Here's my code:
HashMap<String, String[]> map = new HashMap<String, String[]>();
BufferedReader file = new BufferedReader(new FileReader("test.txt"));
String str;
while ((str = file.readLine()) != null) {
// ... logic
map.put(key, value);
}
System.out.println("number of keys: " + map.size());
for (Map.Entry<String, String[]> entry : map.entrySet()) {
for (String value : entry.getValue()) {
System.out.println(entry.getKey() + ": " + value);
}
}
file.close();
You can try the following code:
public void mapPermute(Map<String, String[]> map, String currentPermutation) {
String key = map.keySet().iterator().next(); // get the topmost key
// base case
if (map.size() == 1) {
for (String value : map.get(key)) {
System.out.println(currentPermutation + key + "=" + value);
}
} else {
// recursive case
Map<String, String[]> subMap = new HashMap<String, String[]>(map);
for (String value : subMap.remove(key)) {
mapPermute(subMap, currentPermutation + key + "=" + value + ", ");
}
}
}
No guarantees on memory efficiency or speed. If you want to preserve the order of the keys in the map, you will have to pass in a TreeMap and change the code to use a TreeMap under the recursive case.
As the base case suggests, I'm assuming you have at least one entry in your map.
You can obtain a Cartesian product of map key-value combinations using a map and reduce approach.
Try it online!
Map<String, String[]> map = Map.of(
"a", new String[]{"3", "4"},
"b", new String[]{"5", "6"});
List<Map<String, String>> comb = map.entrySet().stream()
// Stream<List<Map<String,String>>>
.map(e -> Arrays.stream(e.getValue())
.map(v -> Map.of(e.getKey(), v))
.collect(Collectors.toList()))
// summation of pairs of list into a single list
.reduce((list1, list2) -> list1.stream()
// combinations of inner maps
.flatMap(map1 -> list2.stream()
// concatenate into a single map
.map(map2 -> {
Map<String, String> m = new HashMap<>();
m.putAll(map1);
m.putAll(map2);
return m;
}))
// list of combinations
.collect(Collectors.toList()))
// otherwise, an empty list
.orElse(Collections.emptyList());
// output, order may vary
comb.forEach(System.out::println);
Output, order may vary:
{a=3, b=5}
{a=3, b=6}
{a=4, b=5}
{a=4, b=6}
See also: Cartesian product of map values
It looks to me like you really want a MultiMap. In particular, ArrayListMultimap allows duplicate entries:
ArrayListMultimap<String, String> map = ArrayListMultimap.create();
for each line in file:
parse key k
for each value in line:
parse value v
map.put(k, v);
for (Map.Entry<String, String> entry : map.entries()) {
String key = entry.getKey();
String value = entry.getValue();
}
If you want a cartesian product of maps, you could compute that directly using recursion, or you could iterate over the maps: create a list of iterators and iterate odometer-style; when iterator N reaches its end, advance iterator N+1 and reset iterators 1..N.
Just poked around and found this SO question.
So I'd recommend you use guava's Sets.cartesianProduct for the cartesian product. Here's my poking around code, which you could adapt to your input logic:
String key1 = "a";
Set<Integer> values1 = Sets.newLinkedHashSet(Arrays.asList(1, 2, 3, 4));
String key2 = "b";
Set<Integer> values2 = Sets.newLinkedHashSet(Arrays.asList(5, 6, 7));
String key3 = "c";
Set<Integer> values3 = Sets.newLinkedHashSet(Arrays.asList(8, 9));
List<String> keys = Arrays.asList(key1, key2, key3);
Set<List<Integer>> product = Sets.cartesianProduct(values1, values2, values3);
for (List<Integer> values : product) {
for (int i = 0; i < keys.size(); ++i) {
String key = keys.get(i);
int value = values.get(i);
System.out.print(key + "=" + value + "; ");
}
System.out.println();
}