Printing a sorted treemap(sorted based on values) - java

I have a sorted a TreeMap based on values, and it's printed as shown:
abortion-2
able-2
ab-2
aaron-2
aaa-2
aa-2
a-2
absent-1
absence-1
abraham-1
ability-1
aberdeen-1
abc-1
But it seems like the words with same values are being printed in the reverse sorted order:
"abortion, able, ab, aaron, aaa, aa, a" instead of "a, aa, aaa, aaron, ab, able abortion" and so on.
I have even thought of adding each set of keys having same value to the TreeSet and print it out, but I couldn't iterate it based on next values.
Here is the comparator that I'm passing on to the TreeMap. Can anybody help me correcting the code to print it in the correct order?
public class MyComparator implements Comparator<String>{
Map<String, Integer> tiedMap;
public MyComparator(Map<String, Integer> map){
this.tiedMap = map;
}
public int compare(String a, String b){
if(tiedMap.get(a)>=tiedMap.get(b)){
return -1;
}
else
return 1;
}
}
And here is how I'm trying to print it:
Iterator it = tree.entrySet().iterator();
for(int i=0; i<n; i++){
if(it.hasNext()){
Map.Entry pairs = (Map.Entry)it.next();
System.out.println(pairs.getKey()+"-"+pairs.getValue());
}
}
Edit: I'm reading the input into a TreeMap, and then passing it to another TreeMap.
Edit: Code that creates TreeMaps:
Map<String, Integer> map = new TreeMap<String, Integer>();
Words t = new Words();
MyComparator comp = w.(new MyComparator(map));
Map<String, Integer> tree = new TreeMap<String, Integer>(comp);
int size = Integer.parseInt(buffer.readLine());
for(int i = size; i>0; i--){
reader = buffer.readLine();
if(map.get(reader)!=null){
map.put(reader, map.get(reader)+1);
}
else
map.put(reader, 1);
}
tree.putAll(map);

Your comparator will return the entries sorted in reverse order based on their value alone. Is this what you want?
Also, if you want the entries in a more predictable order you should also compare the keys:
public int compare(String a, String b)
{
Integer aVal = tiedMap.get(a);
Integer bVal = tiedMap.get(b);
if (aVal > bVal)
{
return 1; // or -1 for descending order
}
else if (aVal < bVal)
{
return -1; // or 1 for descending order
}
else
{
// if values are equivalent compare on key as well
return a.compareTo(b);
// or for descending order:
// return b.compareTo(a);
}
}

if(tiedMap.get(a)>=tiedMap.get(b)){
return -1;
}
else
return 1;
You should modify your code to return 0 when values are same. This will ensure that the relative ordering between your original keys is not changed. If that does not work you can add additional code like:
if (tiedMap.get(a) == tiedMap.get(b))
return a.compareTo(b);

Actually by using comparator, you can sort your HashMap, TreeMap into ascending as well descending order.
Try out this:
// sort list based on comparator
Collections.sort(list, new Comparator() {
public int compare(Object o1, Object o2) {
return ((Comparable) ((Map.Entry) (o2)).getValue())
.compareTo(((Map.Entry) (o1)).getValue());
}
});
This will give the output into descending order. By interchanging the o2 and o1 only, you will get them sorted in ascending order.

I am not sure if I understand your expectation/implementation completely but I think you need to character-to-character comparison between string a and b in compare function.

Related

Find the number of repetitions of a character in a given string and sort them?

Problem statement: Find the repetition of the alphabet characters in a given string (all lower case from a-z only) and sort them from lowest to highest.
If two character have the same number of repetitions, the character with the greater ASCII value is considered to be smaller.
Although the problem is easy, I was trying to use Comparator to sort the final answer instead of using my own sorting. Here is what I did:
private static void importantString(String context) {
HashMap<Character, Integer> importance = new HashMap<Character, Integer>();
String alpha="abcdefghijklmnopqrstuvwxyz";
for(int i=0; i<26; i++){
importance.put(alpha.charAt(i),0);
}
for(int i=0; i<context.length(); i++){
char temp = context.charAt(i);
Integer val = importance.get(temp);
importance.put(temp,++val);
}
//To sort
ArrayList<Map.Entry<Character, Integer>> l = new ArrayList(importance.entrySet());
Collections.sort(l, new Comparator<Map.Entry<Character, Integer>>(){
public int compare(Map.Entry<Character, Integer> o1, Map.Entry<Character, Integer> o2) {
if (o1.getValue()==o2.getValue()){
if (o1.getKey() > o2.getKey()){
return -1;
}
else{
return 1;
}
}
else {
return o1.getValue().compareTo(o2.getValue());
}
}});
System.out.println(l);
for (Entry<Character, Integer> m: l){
System.out.print(m.getKey()+" ");
}
System.out.println();
}
Now, this definitley works for smaller test cases. For example,
However, I had a very large test case this is what I get (my sorted array is this since the test case string is very large):
[r=38083, p=38223, v=38223, f=38268, e=38269, u=38306, z=38320, k=38341, g=38342, c=38396, o=38418, q=38418, b=38422, n=38467, x=38476, y=38477, l=38525, m=38534, w=38575, d=38580, a=38619, s=38648, t=38653, h=38787, j=38791, i=38839]
Notice, o=38418 and q=38418. In this case 'q' should have a lower precedence than o since it has a higher ASCII value. Yet it does not reflect.
For smaller test cases like oooqqq, I do get the correct results. Any explainations why?
Your issue is this line:
if (o1.getValue()==o2.getValue()){
You're using reference equality here. Since both sides of == are of type Integers they are tested for reference equality, but only for values -128 <= value <= 127 those Integers are guarantied to be the same objects (see Integer.valueOf which is used for autoboxing here: importance.put(temp,++val);)
You could simply replace this with a comparison of the int values:
if (o1.getValue().intValue()==o2.getValue().intValue()){
Furthermore you could also rewrite the method by using Integer.compareTo and Character.compareTo:
public int compare(Map.Entry<Character, Integer> o1, Map.Entry<Character, Integer> o2) {
int res = o1.getValue().compareTo(o2.getValue());
return res == 0 ? o2.getKey().compareTo(o1.getKey()) : res;
}
That's because you don't have the "equal" case in your comparator for letters, also the Integer class should be compared using equals, not ==.
Change the Comparator and sorting part to something like this:
Collections.sort(l, new Comparator<Map.Entry<Character, Integer>>(){
public int compare(Map.Entry<Character, Integer> o1, Map.Entry<Character, Integer> o2) {
if (o1.getValue().equals(o2.getValue())){
return -o1.getKey().compareTo(o2.getKey()))
}
else {
return o1.getValue().compareTo(o2.getValue());
}
}});

Best way to prioritize certain Strings while sorting a Map

I'd like to sort a map with strings, so that certain strings are prioritized and the rest is ordered as usual.
Like this:
"Dan", "value" //priority 1
"Eric", "value" //priority 2
"Ann", "value" //priority 3
"Bella", "value" //no priority
"Chris", "value" //no priority
Just like in this question.
I'm using a TreeMap and my current compare method looks like this:
public int compare(String o1, String o2) {
if (o1.equals(o2)) return 0;
if (o1.equals("Dan")) return -1;
if (o2.equals("Dan")) return 1;
if (o1.equals("Eric")) return -1;
if (o2.equals("Eric")) return 1;
if (o1.equals("Ann")) return -1;
if (o2.equals("Ann")) return 1;
else return o1.compareTo(o2);
}
As you can see, this gets quite cumbersome with more prioritized Strings.
Is there a better way to do this?
Solution (thanks to amit for the idea):
Use a 2nd map to store the priorities:
TreeMap<String, Integer> prio = new TreeMap<>();
prio.put("Dan", 1);
prio.put("Eric", 2);
prio.put("Ann", 3);
comparator = new Comparator<String>() {
#Override
public int compare(String o1, String o2) {
if (prio.containsKey(o1)) {
if (prio.containsKey(o2)) {
return prio.get(o1).compareTo(prio.get(o2));
} else return -1;
} else if (prio.containsKey(o2)) {
return 1;
} else return o1.compareTo(o2);
}
};
Use a 2nd map:
Map<String,Integer> prio where the value is the priority of each string.
In your comparator - first compare according to prio.get(o1).compareTo(prio.get(o2))1, and only if the result is 0, fall back to regular String's compareTo().
It is important that prio does not change after your map is created, otherwise your map will be a complete chaos without an ability to find and insert elements properly.
(1) Make sure both elements exist first in prio, and if one does not - resolve.

While sorting the map based on value, some values are missing. What causes this weird behaviour?

I am trying to sort a map based on word frequency (i.e., based on value). For that I have overridden comparator and passed to TreeMap, but I am getting this weird output.
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 0;
}
}
};
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);
}
}
Output:
Before sorting: {two=2, one=1, three=3, four=3}
After sorting based on value:{one=1, two=2, three=3}
Expected Output:
{one=1, two=2, four=3,three=3}
Your compare method fails to obey the contract of the Map interface, since it compares values instead of keys. Your implementation causes two keys with the same value to be considered the same key. Therefore your sortedMap doesn't contain the "four" key, which has the same value as the "three" key.
Note that the ordering maintained by a tree map, like any sorted map, and whether or not an explicit comparator is provided, must be consistent with equals if this sorted map is to correctly implement the Map interface. (See Comparable or Comparator for a precise definition of consistent with equals.) This is so because the Map interface is defined in terms of the equals operation, but a sorted map performs all key comparisons using its compareTo (or compare) method, so two keys that are deemed equal by this method are, from the standpoint of the sorted map, equal. The behavior of a sorted map is well-defined even if its ordering is inconsistent with equals; it just fails to obey the general contract of the Map interface.
TreeMap reference
You can fix this problem by comparing the keys when the values are equal :
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);
}
}
};
This should give you an output of :
After sorting based on value:{one=1, two=2, four=3, three=3}
Since four<three based on the natural ordering of Strings.
Because of your compare() is consider values only in the Map. Then three=3, four=3 has same value 3. Then those consider as duplicates when they add to TreeMap.
That's because your implementation is telling TreeMap that map[three] and map[four] are essentially the same element, because they are "equal" to each other according to your comparator.
Change "return 0" in Comparator to "return s1.compareTo(s2)", and you'll have
Before sorting: {two=2, one=1, three=3, four=3}
After Sorting based on value:{one=1, two=2, four=3, three=3}
(I believe you can figure out why "four" comes before "three" in this case)

Sorting Descending order: Java Map

What I want to do is sort a map by value. I went over many questions that are available on the stackoverflow site and found out following solution that does what I want but missing a small thing.
Link1: Sorting Map
But the issue I am running into is that by default this is sorted by ascending order by value. I want to order it by descending order:
So what I did was I created a class that implements a comparator
class MyComparator implements Comparator {
Map map;
public MyComparator(Map map) {
this.map = map;
}
public int compare(Object o1, Object o2) {
return ((Integer) map.get(o2)).compareTo((Integer) map.get(o1));
}
}
And then I pass my map to the treemap,
MyComparator comp = new MyComparator(myMap);
Map<String, Integer> newMap = new TreeMap(comp);
newMap.putAll(myMap);
This seems like bad approach because I feel this is inefficient. Is there a way to change the solution in the link to do ordering on descending order by default.
You should use new TreeMap<>(Collections.reverseOrder());.
Map<String, Integer> newMap = new TreeMap<>(Collections.reverseOrder());
newMap.putAll(myMap);
or to reverse an existing comparator like the value-comparator Collections.reverseOrder(comparator). It works like your approach swapping the two objects before invoking compare/compareTo.
TreeMap<Long,String> treeMap = new TreeMap<Long,String>();
NavigableMap <Long, String> nmap = treeMap.descendingMap();
Set<Long, String> set = nmap.entrySet();
Iterator<Long, String> iterator = set.iterator();
now u can iterate over iterator and extract the value using iterator.hasNext() and iterator.next() methods ......
This will work :
TreeMap<Integer, Integer> reverseInteger=new TreeMap<>(new Comparator<Integer>() {
#Override
public int compare(Integer o1, Integer o2) {
return o2>o1?1:o2==o1?0:-1;
}
});
You could simply invert the return value of your compare method by adding a minus sign at the beginning:
return -((Integer) map.get(o2)).compareTo((Integer) map.get(o1));
To change the solution in the link to sort by descending order, just reverse the condition:
...
// 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; // For ascending, return -1;
} else {
return -1; // For ascending, return 1;
} // returning 0 would merge keys
}
...

sort hashtable by values

If I have a Hashtable and I want to sort it by the value, i.e: integer in a descending order. How can I do this and be able to print through all of the key - value pair?
Transfer as List and sort it:
public static void sortValue(Hashtable<?, Integer> t){
//Transfer as List and sort it
ArrayList<Map.Entry<?, Integer>> l = new ArrayList(t.entrySet());
Collections.sort(l, new Comparator<Map.Entry<?, Integer>>(){
public int compare(Map.Entry<?, Integer> o1, Map.Entry<?, Integer> o2) {
return o1.getValue().compareTo(o2.getValue());
}});
System.out.println(l);
}
SortedMap allows you to either specify a comparator, or if not use the natural ordering of elements, of which the inverse will be fine for Integers. The following prints in descending sorted order:
SortedMap<Integer, Object> map = new TreeMap<Integer, Object>(new Comparator<Integer>() {
public int compare(Integer o1, Integer o2) {
return o2.compareTo(o1);
}
});
map.put(2, "value2");
map.put(3, "value3");
map.put(1, "value1");
for (Map.Entry<Integer, Object> nextEntry : map.entrySet()) {
System.out.println(nextEntry.getKey() + " : " + nextEntry.getValue());
}
Hashtables are not sorted. So you need to make a copy of the hash table's key set, sort it, and retrieve the values from the hashtable by iterating through the keys in your sorted list.
Or use a sorted hash table substitute, such as TreeMap; that would avoid having to make the copy of the key set.
If you really mean "how do I do this", then the answer is to just add all of them to a TreeMap and then iterate through it, or add all of them to an ArrayList and then sort it.
If you mean "how do I do this efficiently", I believe the answer is that it's not possible to get any more efficient than above.
This question may have some more info.
Refer to below link
Sorting HashMap by values
or
How to sort a treemap based on its values?
Both are implementation for sorting an hashmap based on value in ascending or descending order
An inefficient way of doing it if you don't understand the above code.
public static void sortHashtable1 (Hashtable <Integer,Double> t,int count)
{
double a[]=new double[count];
int i=0;
for (int ss : t.keySet())
{
a[i]=t.get(ss);
i++;
}
Arrays.sort(a);
outer:for(int j=a.length-1;j>=0;j--)
{
for(int ss : t.keySet())
if(t.get(ss)==a[j])
{
System.out.println(ss+" "+a[j]);
a[j]=-1;
t.put(ss, -1.0);
continue outer;
}
}
}

Categories