I had to code my own ´Filter` for a medal table, which I put in a
HashMap <String, MedalsOfCountry> unsortedList.
The key is the code of a country, for example:"USA" and the MedalsOfCountry is the object for the specific country with the fields: goldCount, silverCount, bronzeCount. MedalsOfCountry is a static inner class, which I coded and looks like this:
static class MedalsOfCountry extends Filter {
int goldCount;
int silverCount;
int bronzeCount;
Medal medalType;
MedalsOfCountry(Medal m, int count) {
medalType = m;
if (medalType == Medal.GOLD) {
goldCount += count;
} else if (medalType == Medal.SILVER) {
silverCount+= count;
} else {
bronzeCount += count;
}
}
public int compareTo(MedalOfCountry medals) {
if(this.goldCount == medals.goldCount) {
return this.silverCount - medals.silverCount;
} else if(this.silverCount == medals.silverCount) {
return this.bronzeCount - medals.bronzeCount;
}
return this.goldCount - medals.goldCount;
}
Anyway I tried to sort my HashMap like this:
Map<String, MedalsOfCountry> sortedList = new TreeMap<String, MedalsOfCountry>(new Comparator<String>() {
#Override
public int compare(String land1, String land2) {
return unsortedList.get(land1).compareTo(unsortedList.get(land2));
}
Why does my sorting not work?
A TreeMap is sorted by keys:
The map is sorted according to the natural ordering of its keys, or by a Comparator provided at map creation time, depending on which constructor is used.
So using a TreeMap would not be the best choice in your case. You could either use a LinkedHashMap or changing your map to a List.
A LinkedHashMap is sorted by insertion order:
This linked list defines the iteration ordering, which is normally the order in which keys were inserted into the map (insertion-order).
Map<String, MedalsOfCountry> sorted = unsorted.entrySet().stream()
.sorted((entry0, entry1) -> entry0.getValue().compareTo(entry1.getValue()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (v0, v1) -> {
throw new IllegalArgumentException("duplicate keys are not possible here");
}, LinkedHashMap::new));
To improve this I would recommend to implement the Comparable interface in your Object class:
static class MedalsOfCountry implements Comparable<MedalsOfCountry> {
int goldCount;
int silverCount;
int bronzeCount;
MedalsOfCountry(int count) {
goldCount += count;
}
#Override
public int compareTo(MedalsOfCountry medals) {
int compareGold = this.goldCount - medals.goldCount;
if (compareGold != 0) {
return compareGold;
}
int compareSilver = this.silverCount - medals.silverCount;
if (compareSilver != 0) {
return compareSilver;
}
return this.bronzeCount - medals.bronzeCount;
}
}
You then can use the following to create the sorted map:
Map<String, MedalsOfCountry> sorted = unsorted.entrySet().stream()
.sorted(Comparator.comparing(Map.Entry::getValue))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (v0, v1) -> {
throw new IllegalArgumentException("duplicate keys are not possible here");
}, LinkedHashMap::new));
Additionally you can improve your compareTo() method by using a comparator to define multiple steps:
#Override
public int compareTo(MedalsOfCountry medals) {
return Comparator.comparingInt(MedalsOfCountry::getGoldCount)
.thenComparingInt(MedalsOfCountry::getSilverCount)
.thenComparingInt(MedalsOfCountry::getBronzeCount)
.compare(this, medals);
}
To also can use the Comparator.reversed() method to reverse the order of your stream:
Map<String, MedalsOfCountry> sorted = unsorted.entrySet().stream()
.sorted(Comparator.comparing(Map.Entry<String, MedalsOfCountry>::getValue).reversed())
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (v0, v1) -> {
throw new IllegalArgumentException("duplicate keys are not possible here");
}, LinkedHashMap::new));
Related
I have a HashMap that map character to an Integer. In order to sort it by value I wrote my comparator and I'm using TreeMap. But I am missing the value. I checked that for String "tree". My map 'chars' after for each loop looks like {r=1, t=1, e=2} and tree after putAll (two lines later) is {e=2, r=1}. What is happening to char 't'? Why is it missed? And how can I change it?
class ValueComparator implements Comparator<Character> {
private Map<Character, Integer> map;
public ValueComparator(Map<Character, Integer> map) {
this.map = map;
}
public int compare(Character a, Character b) {
return map.get(b).compareTo(map.get(a));
}
}
public String frequencySort(String s) {
if (s.length() <= 1) return s;
HashMap<Character,Integer> chars = new HashMap<Character,Integer>();
for(Character c : s.toCharArray()){
if (chars.containsKey(c)){
chars.put(c,chars.get(c)+1);
}
else {
chars.put(c,1);
}
}
TreeMap<Character,Integer> tree = new TreeMap<Character,Integer>(new ValueComparator(chars));
tree.putAll(chars);
/**
* rest of the code
**/
}
Your ValueComparator treats entries with the same count as duplicates. A simple fix is to use the key as a tie-breaker:
public int compare(Character a, Character b) {
int result = map.get(b).compareTo(map.get(a));
return result != 0 ? result : a.compareTo(b);
}
Alternatively, you can use streams to build the frequency map, sort it and store it an ordered LinkedHashMap:
Map<Character, Integer> counts = s.chars()
.mapToObj(i -> (char)i)
.collect(Collectors.groupingBy(Function.identity(), Collectors.summingInt(c -> 1)))
.entrySet()
.stream()
.sorted(Collections.reverseOrder(Entry.comparingByValue()))
.collect(Collectors.toMap(Entry::getKey, Entry::getValue, (a, b) -> b, LinkedHashMap::new));
I'm trying to add the keys in the hash map (it is a static variable of the class) playerToBook to a priority queue sorted by the values and I'm getting an error on line
PriorityQueue<Integer> maxBooksList = new PriorityQueue<>(playerToBook.size(), comparator);
saying PriorityQueue cannot be applied to the comparator.
public static PriorityQueue<Integer> playerWithMaxBooks() {
Comparator<HashMap.Entry<Integer, Integer>> comparator = new fCompare();
PriorityQueue<Integer> maxBooksList = new PriorityQueue<>(playerToBook.size(), comparator);
Iterator it = playerToBook.entrySet().iterator();
while (it.hasNext()) {
HashMap.Entry pair = (HashMap.Entry) it.next();
maxBooksList.add((Integer)pair.getKey());
}
return maxBooksList;
}
public static class fCompare implements Comparator<HashMap.Entry<Integer, Integer>>
{
#Override
public int compare(HashMap.Entry<Integer, Integer> e1, HashMap.Entry<Integer, Integer> e2)
{
if (e1.getValue() < e2.getValue())
{
return -1;
}
if (e1.getValue() > e1.getValue())
{
return 1;
}
return 0;
}
}
Please help!
I have finally gotten my code to where it works. Although it isnt the easiest to read.
I am reading from a text file that has
Date/time Tip from xxx
tip totalAmount
My code now takes the amount tipped by a person and adds them together. eg X tip 10, X tip 20, X tip 30, Y tip 200, Z tip 30, Z tip 40 and outputs
X=60
Y=200
Z=70
I did this by turning my Map< String,Integer> into an Object[] tipsPerPerson
So how would I go about sorting this Object[] tipsPerPerson into something a bit easier to read (theres over 2000 names) a bit like this
Y=200
Z=70
X=60
Here is a portion of the code that im stuck at
import java.io.BufferedReader;
import java.io.FileReader;
import java.util.HashMap;
import java.util.Map;
public class Tip {
public static void main(String[] args)
{
int lineNumber = 1;
Map<String, Integer> tipsByName = new HashMap<String, Integer>();
String fileName = "C:\\Users\\David\\Desktop\\tips.txt";
System.out.println("Reading text from file");
try {
FileReader inputFile = new FileReader(fileName);
BufferedReader bufferedReader = new BufferedReader(inputFile);
String line;
String currentTipper = null;
while ((line = bufferedReader.readLine()) != null) {
if (lineNumber % 2 != 0) {
final String tipperName = line.substring(line.indexOf("from ") + 5);
currentTipper = tipperName;
} else {
final Integer tipValue = Integer.parseInt(line.substring(0, line.indexOf("\t")));
// here we store the tip in the map. If we have a record we sum, else
// we store as is
tipsByName.put(currentTipper, (tipsByName.get(currentTipper) == null ? 0 : tipsByName.get(currentTipper))
+ tipValue);
}
lineNumber++;
}
bufferedReader.close();
Object[] tipsName = tipsByName.entrySet().toArray();
for (int i = 0; i < tipsByName.size(); i++) {
System.out.println(tipsName[i]); // output the map
}
} catch (Exception e) {
System.out.println("Error while reading file line by line: " + e.getMessage());
}
}
}
Just dump the entries into a list and sort them using a Comparator:
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());
}
});
Note that b is compared to a, rather than the other way around, to give us reverse order (largest to smallest).
All done in two lines. If you then iterate over the list, it will be in the order you wanted. Use getKey() and getValue() to print, or simply use the default toString() of the Entry:
for (Map.Entry<String, Integer> entry : entries)
System.out.println(entry + '\n');
An advice: use the map and d'ont make any array convertions.
The map class gives you all that you need.
You can iterate over the keys or over the values or over both like you do right now.
Just use the key values of your map and pass it as an argument to
Collections.sort(). This works because the returned key set is of type Collection
which can be passed to the Collections.sort() method.
Than it will be ordered in alphabetical order.
If you want something else just pass an additonal Comparator class to the Collection.sort()
or wrap your object within a customized class which implements the
interface Comparable.
After that you just iterate throught you keys and
invoke map.get(key) method.
Example:
Iterator<String> sortedKeys = map.keySet().iteator();
for (String key :keys) {
sysout(key: "+key+" value : "+ map.get(key));
}
Hope it was thge answer you needed.
Ouch. Don't do that! Try this instead...
// Get the Entries.
java.util.Set<Entry<String, Integer>> entrySet = tipsByName
.entrySet();
// Make a SortedSet with a Custom Comparator.
SortedSet<Entry<String, Integer>> sortedSet = new TreeSet<Entry<String, Integer>>(
new Comparator<Entry<String, Integer>>() {
public int compare(
Entry<String, Integer> o1,
Entry<String, Integer> o2) {
if (o1 == o2) {
return 0;
} else if (o1 == null) {
return -1;
} else if (o2 == null) {
return 1;
}
return o1.getValue().compareTo(
o2.getValue());
}
});
// Add the Entries to the SortedSet.
for (Entry<String, Integer> entry : entrySet) {
sortedSet.add(entry);
}
// Iterate the sortedSet.
Iterator<Entry<String, Integer>> iter = sortedSet
.iterator();
while (iter.hasNext()) {
System.out.println(iter.next()); // print your sorted items.
}
I'm trying to get results HashMap sorted by value.
This is HashMap's keys and values:
map.put("ertu", 5);
map.put("burak", 4);
map.put("selin", 2);
map.put("can", 1);
I try to get results like this:
1 = can
2 = selin
4 = burak
5 = ertu
Here is my code:
import java.util.*;
public class mapTers {
public static void main(String[] args) {
HashMap<String, Integer> map = new HashMap<String, Integer>();
map.put("ertu", 5);
map.put("burak", 4);
map.put("selin", 2);
map.put("can", 1);
Integer dizi[] = new Integer[map.size()];
Set anahtarlar = map.keySet();
Iterator t = anahtarlar.iterator();
int a = 0;
while (t.hasNext()) {
dizi[a] = map.get(t.next());
a++;
}
Arrays.sort(dizi);
for (int i = 0; i < map.size(); i++) {
while (t.hasNext()) {
if (dizi[i].equals(map.get(t.next()))) {
System.out.println(dizi[i] + " = " + t.next());
}
}
}
}
}
You can sort the entries as follows (but note this won't sort the map itself, and also HashMap cannot be sorted) -
List<Map.Entry<String, Integer>> entryList = new ArrayList<Map.Entry<String, Integer>>(map.entrySet());
Collections.sort(entryList, new Comparator<Map.Entry<String, Integer>>() {
#Override
public int compare(Entry<String, Integer> o1, Entry<String, Integer> o2) {
return o1.getValue().compareTo(o2.getValue());
}
});
Every time that you call t.next(), the iterator's pointer is moved forward. Eventually, the iterator reaches the end. You need to reset the iterator. Also, calling t.next() twice moves the pointer twice.
Here's my solution:
import java.util.*;
public class mapTers
{
public static void main(String[] args)
{
HashMap<String, Integer> map = new HashMap<String, Integer>();
map.put("ertu", 5);
map.put("burak", 4);
map.put("selin", 2);
map.put("can", 1);
Integer dizi[] = new Integer[map.size()];
Set anahtarlar = map.keySet();
Iterator t = anahtarlar.iterator();
int a = 0;
while (t.hasNext())
{
dizi[a] = map.get(t.next());
a++;
}
Arrays.sort(dizi);
for (int i = 0; i < map.size(); i++)
{
t = anahtarlar.iterator();
while (t.hasNext())
{
String temp = (String)t.next();
if (dizi[i].equals(map.get(temp)))
{
System.out.println(dizi[i] + " = " + temp);
}
}
}
}
}
You cannot do that from a Map. At least not directly.
Retrieve the keys/entries, get all the map data in a more suitable structure (hint: a class that encapsulates both attributes and is is stored in a sortable (hint2: SortedSet, List)) and sort.
Do not forget to extend Comparable (and implement compareTo) or, otherwise, create a Comparator.
This is one of the solutions take from: https://stackoverflow.com/a/13913206/1256583
Just pass in the unsorted map, and you'll get the sorted one.
private static Map<String, Integer> sortByComparator(Map<String, Integer> unsortMap, final boolean order) {
List<Entry<String, Integer>> list = new LinkedList<Entry<String, Integer>>(unsortMap.entrySet());
// Sorting the list based on values
Collections.sort(list, new Comparator<Entry<String, Integer>>() {
public int compare(Entry<String, Integer> o1, Entry<String, Integer> o2) {
if (order) {
return o1.getValue().compareTo(o2.getValue());
}
else {
return o2.getValue().compareTo(o1.getValue());
}
}
});
// Maintaining insertion order with the help of LinkedList
Map<String, Integer> sortedMap = new LinkedHashMap<String, Integer>();
for (Entry<String, Integer> entry : list) {
sortedMap.put(entry.getKey(), entry.getValue());
}
return sortedMap;
}
To print, do a simple iteration over the entry set:
public static void printMap(Map<String, Integer> map) {
for (Entry<String, Integer> entry : map.entrySet()) {
System.out.println("Key : " + entry.getKey() + " Value : "+ entry.getValue());
}
}
You probably have the wrong data structure for this problem. Either:
Reverse the map so the integers are the keys and the words the values and make the map a SortedMap, or
Use a bidirectional map as provided by libraries like Google Guava.
Reversed Map
private final SortedMap<Integer, String> TRANSLATIONS;
static {
SortedMap<Integer, String> map = new TreeMap<>();
map.put(1, "can");
// ...
TRANSLATIONS = Collections.unmodifiableSortedMap(map);
}
Guava BiMap
private final BiMap TRANSLATIONS =
new ImmutableBiMap.Builder<String, Integer>()
.put("ertu", 5);
.put("burak", 4);
.put("selin", 2);
.put("can", 1);
.build();
Then, iterate over a sorted version of the key set or value set as needed. For example,
TRANSLATIONS.inverse.get(4); // "burak"
I'm just curious. What language are your strings in?
Thanks for the help from Zirak In my previous post i implemented the following in JavaScript:
var arr1 =[0,1,2,3];
var arr2 =["ac", "bc", "ad", "e"];
var result = arr1 .sort(function(i, j){return arr2[i].localeCompare(arr2[j])})
document.write(result );
The way to achieve this is quite compact in JavaScript, can a java implementation of this be also achieved by such simplicity? I could only think of implementing the Comparable interface like the following:
public class testCompare {
public static String[] arr2={"ac", "bc", "ad", "e"};
public static Obj[] arr1={new Obj(0), new Obj(1), new Obj(2), new Obj(3)};
static class Obj implements Comparable{
int index=0;
public Obj(int i){
index=i;
}
#Override
public int compareTo(Object o) {
return arr2[index].compareTo(arr2[((Obj)o).index]);
}
}
}
but if the array have X many items, then I will have to create X many Objs, is there another way that I could achieve this more simply? Another question is, if I do the above method what would be the time complexity for the sorting both in java and in JavaScript, are they all O(n^2)? Thanks a lot
public class MyComparator implements Comparator<Integer> {
#Override
public int compare(Integer i1, Integer i2) {
return arr2[i1.intValue()].compareTo(arr2[i2.intValue()]);
}
}
Arrays.sort(arr1, new MyComparator());
This is the equivalent of the JavaScript sort. The Comparator object is used as the callback function is used in JavaScript.
Try using a TreeMap<String, Integer> (assuming you want to sort integers) which means all entries are sorted by their string key:
SortedMap<String, Integer> map = new TreeMap<String, Integer>();
map.put("ac", 0);
map.put("bc", 1);
map.put("ad", 2);
map.put("e", 3);
for( Map.Entry<String, Integer> entry : map.entrySet() )
{
System.out.println(entry.getKey() + " - " + entry.getValue());
}
Output:
ac - 0
ad - 2
bc - 1
e - 3
To sort an array and get the new order of the previous indices you could iterate over the array and add the indices as Integer objects to the map:
String[] input = {"ab", "bc", "ad" , "e" };
SortedMap<String, Integer> map = new TreeMap<String, Integer>();
for( int i = 0; i < input.length; ++i )
{
map.put(input[i], i); //or use values from another array, e.g. map.put(inputKeys[i], inputValues[i]);
}
If you need to sort the keys by anything else but the natural order, you can add a Comparator<String> to the TreeMap constructor.
public class SortA1byA2array
{
public static void main (String[] args)
{
int[] arr1={2,1,2,5,7,1,9,8,3,6,8,8};
int[] arr2={2,1,8,3};
TreeMap hm=new TreeMap();
int count=1;
for(int i=0;i<arr1.length;i++){
if(hm.containsKey(arr1[i])){
hm.put(arr1[i], ++count);
}
else{
count=1;
hm.put(arr1[i],count);
}
}
for(int i=0;i<arr2.length;i++){
if(hm.containsKey(arr2[i])){
for(int j=0;j<(Integer)hm.get(arr2[i]);j++){
System.out.println(arr2[i]);
}
hm.remove(arr2[i]);
}
}
Iterator it = hm.entrySet().iterator();
while (it.hasNext()) {
Map.Entry pairs = (Map.Entry)it.next();
System.out.println(pairs.getKey());
it.remove();
}
}
}
In response to the second part of you question: Arrays.sort in Java has guaranteed O(n log n) time complexity, as specified in the API.