sorting on map based on values - java

How to sort a hash map based on values and if the values are same then the sorting should be on the key.
I tried to use a comparator, but its not giving expected results.
I want the result to be like this
{Bajaj=8.0, Tata=7.99, Maruthi=6.34, Kmart=5.99, Honda=5.78,
Adidas=4.99, Ford=3.99, Nike=3.99, Sears=3.99, Suzuki=3.99,
Apple=2.99, Puma=1.99}
Here's the complete source code:
import java.util.*;
public class Test {
public static void main(String[] args) {
HashMap<String, Double> map = new HashMap<String, Double>();
ValueComparator bvc = new ValueComparator(map);
TreeMap<String, Double> sorted_map = new TreeMap<String, Double>(bvc);
map.put("Adidas", 4.99);
map.put("Nike", 3.99);
map.put("Puma", 1.99);
map.put("Ford", 3.99);
map.put("Apple", 2.99);
map.put("Sears", 3.99);
map.put("Kmart", 5.99);
map.put("Tata", 7.99);
map.put("Maruthi", 6.34);
map.put("Honda", 5.78);
map.put("Bajaj", 8.0);
map.put("Suzuki", 3.99);
System.out.println("unsorted map: " + map);
sorted_map.putAll(map);
System.out.println("results: " + sorted_map);
}
}
class ValueComparator implements Comparator<String> {
Map<String, Double> base;
public ValueComparator(Map<String, Double> base) {
this.base = base;
}
// Note: this comparator imposes orderings that are inconsistent with
// equals.
#Override
public int compare(String a, String b) {
if (base.get(a) > base.get(b)) {
return -1;
} else if (base.get(a) == base.get(b)) {
System.out.println();
if (a.compareTo(b) == -1) {
return -1;
} else if (a.compareTo(b) == 1) {
return 1;
} else {
return 0;
}
} else {
return 1;
} // returning 0 would merge keys
}
}

Here you have two problems in your compare implementation. First, you compare boxed Double values with ==:
else if(base.get(a) == base.get(b))
You should replace this with
else if(base.get(a).equals(base.get(b)))
Second, you check a.compareTo(b) for specific values like -1 and 1, but it may return any positive/negative numbers. It's better and simpler just to return the result of a.compareTo(b) instead. Here's the fixed compare method:
public int compare(String a, String b) {
if (base.get(a) > base.get(b)) {
return -1;
} else if (base.get(a).equals(base.get(b))) {
return a.compareTo(b);
} else {
return 1;
} // returning 0 would merge keys
}
If you want to sort the keys with the same value in case-insensitive manner, just use compareToIgnoreCase:
public int compare(String a, String b) {
if (base.get(a) > base.get(b)) {
return -1;
} else if (base.get(a).equals(base.get(b))) {
return a.compareToIgnoreCase(b);
} else {
return 1;
} // returning 0 would merge keys
}

As the sorting order relies on both value and key, use the Map.Entry<String, Double> entries:
List<Map.Entry<String, Double>> entries = new ArrayList<>(base.entrySet());
Collections.sort(entries, new Comparator<Map<String, Double>>() {
...
});

Just little bit twisted your compare method of ValueComparator class. This will first sort on values & if values are same then sorting on keys. Hope this helps.
Output would be something like below:
unsorted map: {Adidas=4.99, Bajaj=8.0, Apple=2.99, Ford=3.99, Puma=1.99, Tata=7.99, Nike=3.99, Suzuki=3.99, Honda=5.78, Kmart=5.99, Maruthi=6.34, Sears=3.99}
results: {Bajaj=8.0, Tata=7.99, Maruthi=6.34, Kmart=5.99, Honda=5.78, Adidas=4.99, Ford=3.99, Nike=3.99, Sears=3.99, Suzuki=3.99, Apple=2.99, Puma=1.99}
import java.util.*;
public class SortValueMap {
public static void main(String[] args) {
HashMap<String, Double> map = new HashMap<String, Double>();
ValueComparator bvc = new ValueComparator(map);
TreeMap<String, Double> sorted_map = new TreeMap<String, Double>(bvc);
map.put("Adidas", 4.99);
map.put("Nike", 3.99);
map.put("Puma", 1.99);
map.put("Ford", 3.99);
map.put("Apple", 2.99);
map.put("Sears", 3.99);
map.put("Kmart", 5.99);
map.put("Tata", 7.99);
map.put("Maruthi", 6.34);
map.put("Honda", 5.78);
map.put("Bajaj", 8.0);
map.put("Suzuki", 3.99);
System.out.println("unsorted map: " + map);
sorted_map.putAll(map);
System.out.println("results: " + sorted_map);
}
}
class ValueComparator implements Comparator<String> {
Map<String, Double> base;
public ValueComparator(Map<String, Double> base) {
this.base = base;
}
// Note: this comparator imposes orderings that are inconsistent with
// equals.
#Override
public int compare(String a, String b) {
if(base.get(a).compareTo(base.get(b)) != 0) {
if (base.get(a) > base.get(b)) {
return -1;
} else {
return 1;
}
}
return a.compareTo(b);
}
}

Related

Sorting HashMap into TreeMap: custom Comparator removes values with the same key

I'm trying to sort a HashMap<String, Long>. I'm have the following code for sorting:
private static class ValueComparator implements Comparator<String>{
HashMap<String, Long> map = new HashMap<String, Long>();
public ValueComparator(HashMap<String, Long> map){
this.map.putAll(map);
}
#Override
public int compare(String s1, String s2) {
if(map.get(s1) > map.get(s2)){
System.out.println("s1: " + s1 + "; s2: " + s2);
return -1;
}
else if (map.get(s1).equals(map.get(s2))) {
return 0;
}
else{
return 1;
}
}
}
private static TreeMap<String, Long> sortMapByValue(HashMap<String, Long> map){
Comparator<String> comparator = new ValueComparator(map);
//TreeMap is a map sorted by its keys.
//The comparator is used to sort the TreeMap by keys.
TreeMap<String, Long> result = new TreeMap<String, Long>(comparator);
result.putAll(map);
System.out.println("DONE sort");
return result;
}
The problem is, when several different keys have the same values, only one of the key makes it into the final map:
EXAMPLE:
public class Test {
public static void main(String[] args) {
HashMap<String, Long> hashMap = new HashMap<>();
hashMap.put("Cat", (long) 4);
hashMap.put("Human", (long) 2);
hashMap.put("Dog", (long) 4);
hashMap.put("Fish", (long) 0);
hashMap.put("Tree", (long) 1);
hashMap.put("Three-legged-human", (long) 3);
hashMap.put("Monkey", (long) 2);
System.out.println(hashMap); //7 pairs
System.out.println(sortMapByValue(hashMap)); //5 pairs
}
}
How would I fix it?
I don't think it's fixable you are using the the maps in an unintended way and breaking contracts. Tree map is expecting to be sorted by the key and the key is expected to be unique so when the compare == 0 it will just override the node's value. You can always implement your own TreeMap and make it do whatever you want it to.
I'm not sure what you want to do with it but I think you need something like
TreeMap<Long,List<String>>
http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/share/classes/java/util/TreeMap.java
if (cpr != null) {
do {
parent = t;
cmp = cpr.compare(key, t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);

How can I have a treeMap which is sorted by value keep being sorted when I change elements?

I have a map of string keys and int values, I to sort them, and keep them sorted when I change values.
I tried using a treemap for the sorted pairs and a normal map for the unsorted paris so I can use it in the comparator, but after a one value passes the other I get a null expection, this is the defenition:
public static TreeMap<String, Long> countryData;
public static ValueComparator bvc;
public static void setCountryData(HashMap<String, Long> map){
bvc = new ValueComparator(map);
countryData = new TreeMap<String, Long>(bvc);
countryData.putAll(map);
System.out.println(Arrays.toString(countyNames));
System.out.println(countryData.values());
}
public static class ValueComparator implements Comparator<String> {
Map<String, Long> base;
public ValueComparator(Map<String, Long> base) {
this.base = base;
}
public int compare(String a, String b) {
if(base.get(a).equals(base.get(b))){
return 0;
}
if (base.get(a) > base.get(b)) {
return -1;
} else {
return 1;
}
}
}
this is how I change the values:
General.bvc.base.put(country, newValue);
General.countryData.put(country, newValue);
after one value passes another and I try to acces it, I get a null, how can I Do this?
How can I have a TreeMap which is sorted by value ..
You can't. They are sorted by key.
...
Irrelevant.

How to sort java sort map by values with the compare method already in class?

I want to sort a map by its values. But the compare method is already in the class.
public class Parser implements Comparator<String> {
private Map<String, Integer> frequencies;
public Parser() {
frequencies = new HashMap<String, Integer>();
}
public int getCount(String word) {
Integer c = frequencies.get(word);
if (c == null) {
return 0;
}
else {
return c.intValue();
}
}
public int compare(String o1, String o2) {
int count1 = getCount(o1);
int count2 = getCount(o2);
return count1 < count2 ? -1 : count1 > count2 ? 1 : 0;
}
public List<String> getWordsInOrderOfFrequency(){
TreeMap<String,Integer> sorted_map = new TreeMap<String,Integer>();
sorted_map.putAll(frequencies);
ArrayList<String> result = new ArrayList<String>(sorted_map.keySet());
return result;
}
}
Here the question is in the getWordsInOrderOfFrequenct() method. I want to sort the keyset by its values after compared.
Here is the code snippet you can observe how I achieved that
public class WordFrequency {
public static String sentence = "one three two two three three four four four";
public static Map<String, Integer> map;
public static void main(String[] args) {
map = new HashMap<>();
String[] words = sentence.split("\\s");
for (String word : words) {
Integer count = map.get(word);
if (count == null) {
count = 1;
} else {
++count;
}
map.put(word, count);
}
Comparator<String> myComparator = new Comparator<String>() {
#Override
public int compare(String s1, String s2) {
if (map.get(s1) < map.get(s2)) {
return -1;
} else if (map.get(s1) > map.get(s2)) {
return 1;
} else {
return s1.compareTo(s2);
}
}
};
SortedMap<String, Integer> sortedMap = new TreeMap<String, Integer>(myComparator);
System.out.println("Before sorting: " + map);
sortedMap.putAll(map);
System.out.println("After Sorting based on value:" + sortedMap);
}
}
I think the trick is to extract the behaviour of your compare method so it can be reused in the sort...
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import static com.google.common.collect.Lists.*;
public class Parser implements Comparator<String> {
private Map<String, Integer> frequencies = new HashMap<String, Integer>();
public int getCount(String word) {
Integer c = this.frequencies.get(word);
if (c == null) {
return 0;
}
else {
return c.intValue();
}
}
public int compare(String o1, String o2) {
return staticCompare(getCount(o1), getCount(o2));
}
private static int staticCompare(int count1, int count2) {
return count1 < count2 ? -1 : count1 > count2 ? 1 : 0;
}
public List<String> getWordsInOrderOfFrequency(){
List<Map.Entry<String, Integer>> entries = newArrayList(frequencies.entrySet());
Collections.sort(entries, (o1, o2) -> staticCompare(getCount(o1.getKey()), getCount(o2.getKey())));
return entries.stream().map(entry -> entry.getKey()).collect(Collectors.toList());
}
}

Get top 10 values in hash map

I am trying to figure out how could I get the top 10 values from the HashMap. I was initially trying to use the TreeMap and have it sort by value and then take the first 10 values however it seems that that is not the option, as TreeMap sorts by key.
I want to still be able to know which keys have the highest values, the K, V of the map are String, Integer.
Maybe you should implement the Comparable Interface to your value objects stored in the hashmap.
Then you can create a array list of all values:
List<YourValueType> l = new ArrayList<YourValueType>(hashmap.values());
Collection.sort(l);
l = l.subList(0,10);
Regards
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
public class Testing {
public static void main(String[] args) {
HashMap<String,Double> map = new HashMap<String,Double>();
ValueComparator bvc = new ValueComparator(map);
TreeMap<String,Double> sorted_map = new TreeMap<String,Double>(bvc);
map.put("A",99.5);
map.put("B",67.4);
map.put("C",67.4);
map.put("D",67.3);
System.out.println("unsorted map: "+map);
sorted_map.putAll(map);
System.out.println("results: "+sorted_map);
}
}
class ValueComparator implements Comparator<String> {
Map<String, Double> base;
public ValueComparator(Map<String, Double> base) {
this.base = base;
}
// Note: this comparator imposes orderings that are inconsistent with equals.
public int compare(String a, String b) {
if (base.get(a) >= base.get(b)) {
return -1;
} else {
return 1;
} // returning 0 would merge keys
}
}
I am afraid you'll have to iterate over the entire map. Heap is a commonly-used data structure for finding top K elements, as explained in this book.
If you are trying to get the 10 highest values of the map (assuming the values are numeric or at least implementing Comparable) then try this:
List list = new ArrayList(hashMap.values());
Collections.sort(list);
for(int i=0; i<10; i++) {
// Deal with your value
}
Let's assume you have a Map, but this example can work for any type of
Map<String, String> m = yourMethodToGetYourMap();
List<String> c = new ArrayList<String>(m.values());
Collections.sort(c);
for(int i=0 ; i< 10; ++i) {
System.out.println(i + " rank is " + c.get(i));
}
I base my answer in this one from sk2212
First you need to implement a descending comparator:
class EntryComparator implements Comparator<Entry<String,Integer>> {
/**
* Implements descending order.
*/
#Override
public int compare(Entry<String, Integer> o1, Entry<String, Integer> o2) {
if (o1.getValue() < o2.getValue()) {
return 1;
} else if (o1.getValue() > o2.getValue()) {
return -1;
}
return 0;
}
}
Then you can use it in a method such as this one for the attribute "hashmap":
public List<Entry<String,Integer>> getTopKeysWithOccurences(int top) {
List<Entry<String,Integer>> results = new ArrayList<>(hashmap.entrySet());
Collections.sort(results, new EntryComparator());
return results.subList(0, top);
}
public static void main(String[] args) {
HashMap<String, Integer> map = new HashMap<String, Integer>();
// Initialize map
System.out.println(getTopKeysWithOccurences(map, 10));
}
public static List<Entry<String,Integer>> getTopKeysWithOccurences(Map mp, int top) {
List<Entry<String,Double>> results = new ArrayList<>(mp.entrySet());
Collections.sort(results, (e1,e2) -> e2.getValue() - e1.getValue());
//Ascending order - e1.getValue() - e2.getValue()
//Descending order - e2.getValue() - e1.getValue()
return results.subList(0, top);
}

TreeMap<String, Integer> object's get method return null value

import java.util.*;
public class Sort {
static class ValueComparator implements Comparator<String> {
Map<String, Integer> base;
ValueComparator(Map<String, Integer> base) {
this.base = base;
}
#Override
public int compare(String a, String b) {
if (base.get(a) >= base.get(b)) {
return 1;
} else {
return -1;
}
}
}
public static void main(String[] args) {
HashMap<String, Integer> map = new HashMap<String, Integer>();
ValueComparator vc = new ValueComparator(map);
TreeMap<String, Integer> sorted = new TreeMap<String, Integer>(vc);
map.put("A", 1);
map.put("B", 2);
sorted.putAll(map);
for (String key : sorted.keySet()) {
System.out.println(key + " : " + sorted.get(key)); // why null values here?
}
System.out.println(sorted.values()); // But we do have non-null values here!
}
}
Output:
A : null
B : null
[1, 2]
BUILD SUCCESSFUL (total time: 0 seconds)
I wonder why we get null values at the first commented line while we do have non-null values as demonstrated by the second commented line.
Edit: #null's version seems not working. I've changed my code as follows:
public int compare(String a, String b) {
if (a.equals(b)) return 0;
if (base.get(a) >= base.get(b)) {
return 1;
} else return -1;
}
It seems to work but I'm not sure.
My guess is that your ValueComparator.compare() method never returns 0, indicating equality, causing the Map.get() method to not find matches.
Change your compare to in this way
public int compare(String a, String b) {
if (base.get(a) > base.get(b)) {
return 1;
}else if(base.get(a) == base.get(b)){
return 0;
}
return -1;
}
Even with your Comparator which is definitely broken the program will work if you change it as
for (Map.Entry e : sorted.entrySet()) {
System.out.println(e.getKey() + " : " + e.getValue());
}

Categories