Sorting Map using Comparator - java

I'm trying Comparator to implement a sort in TreeMap according to a sequence.
final String sequence="People,Object,Environment,Message,Service";
Comparator<String> comparator = new Comparator<String>() {
#Override
public int compare(String key1, String key2) {
int returned = sequence.indexOf(key1) - sequence.indexOf(key2);
if (returned == 0 && !key1.contains(key2))
returned = -1;
return returned;
}
};
List<String> list=new ArrayList<String>();
Map<String,String> lhm = new TreeMap<String,String>(comparator);
// Put elements to the map
lhm.put("Object", "biu");
lhm.put("Message", "nuios");
lhm.put("Service", "sdfe");
lhm.put("People", "dfdfh");
lhm.put("Environment", "qwe");
lhm.put("Other", "names");
lhm.put("Elements", "ioup");
lhm.put("Rand", "uiy");
for(Entry<String, String> entry : lhm.entrySet()) {
System.out.println(entry.getKey());
}
The Output which I'm getting here is
Rand
Elements
Other
People
Object
Environment
Message
Service
The Elements in treemap which equals the sequence are ordered correctly but other elements which are not following the sequence should come after the sequence.My expectation is like following
People
Object
Environment
Message
Service
Rand
Elements
Other
How to implement this?
Suppose If I add some more words to the elements of my TreeMap means my Comparator doesn't even order the elements.Like this
lhm.put("Object IOn", "biu");
lhm.put("Message dfb", "nuios");
lhm.put("Serviceabc", "sdfe");
lhm.put("Peoplexxx", "dfdfh");
lhm.put("Environmentxxx", "qwe");
lhm.put("Other", "names");
lhm.put("Elements", "ioup");
lhm.put("Rand", "uiy");
My Output become
Rand
Elements
Other
Environmentxxx
Peoplexxx
Serviceabc
Message dfb
Object IOn
Somebody help me to rewrite my Comparator to fix this problem?

Here is some simple code that should to the task.
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
public class FixedOrderComparator implements Comparator<String> {
private final Map<String, Integer> index = new HashMap<>();
public FixedOrderComparator(String elements) {
String[] split = elements.split(",");
for (int i = 0; i < split.length; i++) {
index.put(split[i], i);
}
}
#Override
public int compare(String left, String right) {
Integer rankLeft = index.get(left);
Integer rankRight = index.get(right);
if (rankLeft != null && rankRight != null) {
return rankLeft.compareTo(rankRight);
}
if (rankLeft == null && rankRight == null) {
return left.compareTo(right);
}
return Boolean.compare(rankLeft == null, rankRight == null);
}
}

You have to correct your logic used in the comparator.
final String sequence="People,Object,Environment,Message,Service";
System.out.println(sequence.indexOf("People")); // 0
System.out.println(sequence.indexOf("Object")); // 7
System.out.println(sequence.indexOf("Message")); // 26
System.out.println(sequence.indexOf("Environment")); // 14
.indexOf(key1) returns the index of the first character of the String and not the String.
int returned = sequence.indexOf(key1) - sequence.indexOf(key2);
if(returned < 0){
// then it is sorted;
return 1;
}
else{ return -1; }

Related

How to get the index of object by its property in Java list

I would like to get the index of an object in a list by its property in Java.
Example:
List<MyObj> list = new ArrayList<>();
list.add(new MyObj("Ram");
list.add(new MyObj("Girish");
list.add(new MyObj("Ajith");
list.add(new MyObj("Sai");
public class MyObj {
public String name;
public MyObj(String name){
this.name=name;
}
}
Now, I would like to the get the index of an Object which contains the name as "Girish". Please do let me know the code in JAVA.
If you want a solution with stream use this one:
int index = IntStream.range(0, list.size())
.filter(i -> list.get(i).name.equals(searchName))
.findFirst()
.orElse(-1);
In case you have a List, all you can do is to iterate over each element and check required property. This is O(n).
public static int getIndexOf(List<MyObj> list, String name) {
int pos = 0;
for(MyObj myObj : list) {
if(name.equalsIgnoreCase(myObj.name))
return pos;
pos++;
}
return -1;
}
In case you want to increase performance. Then you could implement your own data structure. Note, that key feature is that your key property should be a key of a HashMap and value of HashMap should be index. Then you get O(1) performance.
public static final class IndexList<E> extends AbstractList<E> {
private final Map<Integer, E> indexObj = new HashMap<>();
private final Map<String, Integer> keyIndex = new HashMap<>();
private final Function<E, String> getKey;
public IndexList(Function<E, String> getKey) {
this.getKey = getKey;
}
public int getIndexByKey(String key) {
return keyIndex.get(key);
}
#Override
public int size() {
return keyIndex.size();
}
#Override
public boolean add(E e) {
String key = getKey.apply(e);
if (keyIndex.containsKey(key))
throw new IllegalArgumentException("Key '" + key + "' duplication");
int index = size();
keyIndex.put(key, index);
indexObj.put(index, e);
return true;
}
#Override
public E get(int index) {
return indexObj.get(index);
}
}
Demo:
IndexList<MyObj> list = new IndexList<>(myObj -> myObj.name);
list.add(new MyObj("Ram"));
list.add(new MyObj("Girish"));
list.add(new MyObj("Ajith"));
list.add(new MyObj("Sai"));
System.out.println(list.getIndexByKey("Ajith")); // 2
indexOf() will work if you change the .equals function
I'd suggest just iterating through
int getIndex(String wanted){
for(int i = 0; i<list.size(); i++){
if(list.get(i).name.equals(wanted)){
return i;
}
}
}
indexOf() will return the index of the first occurrence of a value. For example:
int myIndex = list.indexOf("Ram")
(Note though that your arraylist doesn't contain "Ram", it contains an object of type MyObj with a name of "Ram")
Bear in mind ArrayLists start at 0 not one.

How to create combinations of values in Java?

I have the following map: Map<Integer,String[]> map = new HashMap<Integer,String[]>();
The keys are integers and the values are arrays (could also be replaced by lists).
Now, I would like to get all possible combinations of the values among the keys. For example, let's say the map contains the following entries:
key 1: "test1", "stackoverflow"
key 2: "test2", "wow"
key 3: "new"
The combinations consists of
("test1","test2","new")
("test1","wow","new")
("stackoverflow", "test2", "new")
("stackoverflow", "wow", "new")
For this I imagine a method boolean hasNext() which returns true if there is a next pair and a second method which just returns the next set of values (if any).
How can this be done? The map could also be replaced by an other data structure.
The algorithm is essentially almost the same as the increment algorithm for decimal numbers ("x -> x+1").
Here the iterator class:
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.TreeSet;
public class CombinationsIterator implements Iterator<String[]> {
// Immutable fields
private final int combinationLength;
private final String[][] values;
private final int[] maxIndexes;
// Mutable fields
private final int[] currentIndexes;
private boolean hasNext;
public CombinationsIterator(final Map<Integer,String[]> map) {
combinationLength = map.size();
values = new String[combinationLength][];
maxIndexes = new int[combinationLength];
currentIndexes = new int[combinationLength];
if (combinationLength == 0) {
hasNext = false;
return;
}
hasNext = true;
// Reorganize the map to array.
// Map is not actually needed and would unnecessarily complicate the algorithm.
int valuesIndex = 0;
for (final int key : new TreeSet<>(map.keySet())) {
values[valuesIndex++] = map.get(key);
}
// Fill in the arrays of max indexes and current indexes.
for (int i = 0; i < combinationLength; ++i) {
if (values[i].length == 0) {
// Set hasNext to false if at least one of the value-arrays is empty.
// Stop the loop as the behavior of the iterator is already defined in this case:
// the iterator will just return no combinations.
hasNext = false;
return;
}
maxIndexes[i] = values[i].length - 1;
currentIndexes[i] = 0;
}
}
#Override
public boolean hasNext() {
return hasNext;
}
#Override
public String[] next() {
if (!hasNext) {
throw new NoSuchElementException("No more combinations are available");
}
final String[] combination = getCombinationByCurrentIndexes();
nextIndexesCombination();
return combination;
}
private String[] getCombinationByCurrentIndexes() {
final String[] combination = new String[combinationLength];
for (int i = 0; i < combinationLength; ++i) {
combination[i] = values[i][currentIndexes[i]];
}
return combination;
}
private void nextIndexesCombination() {
// A slightly modified "increment number by one" algorithm.
// This loop seems more natural, but it would return combinations in a different order than in your example:
// for (int i = 0; i < combinationLength; ++i) {
// This loop returns combinations in the order which matches your example:
for (int i = combinationLength - 1; i >= 0; --i) {
if (currentIndexes[i] < maxIndexes[i]) {
// Increment the current index
++currentIndexes[i];
return;
} else {
// Current index at max:
// reset it to zero and "carry" to the next index
currentIndexes[i] = 0;
}
}
// If we are here, then all current indexes are at max, and there are no more combinations
hasNext = false;
}
#Override
public void remove() {
throw new UnsupportedOperationException("Remove operation is not supported");
}
}
Here the sample usage:
final Map<Integer,String[]> map = new HashMap<Integer,String[]>();
map.put(1, new String[]{"test1", "stackoverflow"});
map.put(2, new String[]{"test2", "wow"});
map.put(3, new String[]{"new"});
final CombinationsIterator iterator = new CombinationsIterator(map);
while (iterator.hasNext()) {
System.out.println(
org.apache.commons.lang3.ArrayUtils.toString(iterator.next())
);
}
It prints exactly what's specified in your example.
P.S. The map is actually not needed; it could be replaced by a simple array of arrays (or list of lists). The constructor would then get a bit simpler:
public CombinationsIterator(final String[][] array) {
combinationLength = array.length;
values = array;
// ...
// Reorganize the map to array - THIS CAN BE REMOVED.
I took this as a challenge to see whether the new Java 8 APIs help with these kind of problems. So here's my solution for the problem:
public class CombinatorIterator implements Iterator<Collection<String>> {
private final String[][] arrays;
private final int[] indices;
private final int total;
private int counter;
public CombinatorIterator(Collection<String[]> input) {
arrays = input.toArray(new String[input.size()][]);
indices = new int[arrays.length];
total = Arrays.stream(arrays).mapToInt(arr -> arr.length)
.reduce((x, y) -> x * y).orElse(0);
counter = 0;
}
#Override
public boolean hasNext() {
return counter < total;
}
#Override
public Collection<String> next() {
List<String> nextValue = IntStream.range(0, arrays.length)
.mapToObj(i -> arrays[i][indices[i]]).collect(Collectors.toList());
//rolling carry over the indices
for (int j = 0;
j < arrays.length && ++indices[j] == arrays[j].length; j++) {
indices[j] = 0;
}
counter++;
return nextValue;
}
}
Note that I don't use a map as an input as the map keys actually don't play any role here. You can use map.values() though to pass in the input for the iterator. With the following test code:
List<String[]> input = Arrays.asList(
new String[] {"such", "nice", "question"},
new String[] {"much", "iterator"},
new String[] {"very", "wow"}
);
Iterator<Collection<String>> it = new CombinatorIterator(input);
it.forEachRemaining(System.out::println);
the output will be:
[such, much, very]
[nice, much, very]
[question, much, very]
[such, iterator, very]
[nice, iterator, very]
[question, iterator, very]
[such, much, wow]
[nice, much, wow]
[question, much, wow]
[such, iterator, wow]
[nice, iterator, wow]
[question, iterator, wow]

How to get N most often words in given text, sorted from max to min?

I have been given a large text as input. I have made a HashMap that stores each different word as a key, and number of times that occurs as value (Integer).
Now I have to make a method called mostOften(int k):List that return a List that gives the first k-words that from max number of occurrence to min number of occurrence ( descending order ) using the HashMap that I have made before.
The problem is that whenever 2 words have the same number of occurrence, then they should be sorted alphabetically.
The first idea that was on my mind was to swap keys and values of the given HashMap, and put it into TreeMap and TreeMap will sort the words by the key(Integer - number of occurrence of the word ) and then just pop the last/first K-entries from the TreeMap.
But I will have collision for sure, when the number of 2 or 3 words are the same. I will compare the words alphabetically but what Integer should I put as a key of the second word comming.
Any ideas how to implement this, or other options ?
Hints:
Look at the javadocs for the Collections.sort methods ... both of them!
Look at the javadocs for Map.entries().
Think about how to implement a Comparator that compares instances of a class with two fields, using the 2nd as a "tie breaker" when the other compares as equal.
Here's the solution with I come up.
First you create a class MyWord that can store the String value of the word and the number of occurences it appears.
You implement the Comparable interface for this class to sort by occurences first and then alphabetically if the number of occurences is the same
Then for the most often method, you create a new List of MyWord from your original map. You add the entries of this to your List
You sort this list
You take the k-first items of this list using subList
You add those Strings to the List<String> and you return it
public class Test {
public static void main(String [] args){
Map<String, Integer> m = new HashMap<>();
m.put("hello",5);
m.put("halo",5);
m.put("this",2);
m.put("that",2);
m.put("good",1);
System.out.println(mostOften(m, 3));
}
public static List<String> mostOften(Map<String, Integer> m, int k){
List<MyWord> l = new ArrayList<>();
for(Map.Entry<String, Integer> entry : m.entrySet())
l.add(new MyWord(entry.getKey(), entry.getValue()));
Collections.sort(l);
List<String> list = new ArrayList<>();
for(MyWord w : l.subList(0, k))
list.add(w.word);
return list;
}
}
class MyWord implements Comparable<MyWord>{
public String word;
public int occurence;
public MyWord(String word, int occurence) {
super();
this.word = word;
this.occurence = occurence;
}
#Override
public int compareTo(MyWord arg0) {
int cmp = Integer.compare(arg0.occurence,this.occurence);
return cmp != 0 ? cmp : word.compareTo(arg0.word);
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + occurence;
result = prime * result + ((word == null) ? 0 : word.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
MyWord other = (MyWord) obj;
if (occurence != other.occurence)
return false;
if (word == null) {
if (other.word != null)
return false;
} else if (!word.equals(other.word))
return false;
return true;
}
}
Output : [halo, hello, that]
In addition to your Map to store word counts I would use a PriorityQueue of fixed size K (with natural order). It will allow to reach O(N) complexity. Here is a code which use this approach:
In constructor we are reading input stream word by word filling the counters in the Map.
In the same time we are updating priority queue keeping it's max size = K (we need count top K words)
public class TopNWordsCounter
{
public static class WordCount
{
String word;
int count;
public WordCount(String word)
{
this.word = word;
this.count = 1;
}
}
private PriorityQueue<WordCount> pq;
private Map<String, WordCount> dict;
public TopNWordsCounter(Scanner scanner)
{
pq = new PriorityQueue<>(10, new Comparator<WordCount>()
{
#Override
public int compare(WordCount o1, WordCount o2)
{
return o2.count-o1.count;
}
});
dict = new HashMap<>();
while (scanner.hasNext())
{
String word = scanner.next();
WordCount wc = dict.get(word);
if (wc == null)
{
wc = new WordCount(word);
dict.put(word, wc);
}
if (pq.contains(wc))
{
pq.remove(wc);
wc.count++;
pq.add(wc);
}
else
{
wc.count++;
if (pq.size() < 10 || wc.count >= pq.peek().count)
{
pq.add(wc);
}
}
if (pq.size() > 10)
{
pq.poll();
}
}
}
public List<String> getTopTenWords()
{
Stack<String> topTen = new Stack<>();
while (!pq.isEmpty())
{
topTen.add(pq.poll().word);
}
return topTen;
}
}

remove smallest k elements from hashmap in JAVA

I have a hashmap of objects. Each object has two attributes (let say int length and int weight).
I want to remove k elements with the smallest length.
What is the efficient way of doing this?
Map<K, V> map = new HashMap<>();
...
Set<K> keys = map.keySet();
TreeSet<K> smallest = new TreeSet<>(new Comparator<K>(){
public int compare(K o1, K o2) {
return o1.getLength() - o2.getLength();
}
});
smallest.addAll(keys);
for(int x = 0; x < num; x++) {
keys.remove(smallest.pollFirst());
}
Where K is your key type, V is your value type, and num is the number of elements you wish to remove.
If you are doing this frequently, it might be a good idea to use a TreeMap in the first place.
The easiest, but certainly not the most efficient is to create an instance of a TreeMap with provided Comparator for your type, putAll() elements from your map to the map you just created and remove k-elements with help of keySet(). In the end a TreeMap will not contain k-smallest elements.
You didn't mention if the attribute you discriminate on is part of the key or the value, if it's the key then teh treemap discussed above is applicbale.
Otherwise If you need to do this often I'd be inclined to implement my own map, delegating everything in the map interface to a hashmap (or appropriate structure0. Override the add/remove and if necessary iterator, then use the add/remove to maintain a sorted list of the values.
This obviously assumes the values don't change and is highly coupled to your problem.
Keep in mind that TreeMap sorts by the natural ordering of its keys. Hence you can create a key with comparable based on the length of it's value. For example (Since I am on Lunch the code isn't perfect but should get you to what you need):
package com.trip.test;
import java.util.SortedMap;
import java.util.TreeMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ComparisonTest {
private static Logger logger = LoggerFactory.getLogger(ComparisonTest.class);
private static String[] a = {"1","2","3","4"};
private static String[] b = {"A","B","D"};
private static String[] c = {"1","B","D","1","B","D"};
/**
* #param args
*/
static SortedMap<KeyWithLength, String[]> myMap = new TreeMap<KeyWithLength, String[]>();
static {
myMap.put(new KeyWithLength("a", a.length), a);
myMap.put(new KeyWithLength("b", b.length), b);
myMap.put(new KeyWithLength("c", c.length), c);
}
public static void main(String[] args) {
// print Map
logger.info("Original Map:");
int i = 0;
for (String[] strArray: myMap.values() ){
logger.info(String.format("*** Entry %s: ", i++));
printStrings(strArray);
}
// chop off 2 shortest
chopNShortest(myMap, 2);
// print Map
logger.info("ShortenedMap:");
i = 0;
for (String[] strArray: myMap.values() ){
logger.info(String.format("*** Entry %s: ", i++));
printStrings(strArray);
}
}
static void printStrings(String[] strArray){
StringBuffer buf = new StringBuffer();
for (String str: strArray){
buf.append(String.format("%s, ", str));
}
logger.info(buf.toString());
}
static void chopNShortest(SortedMap<KeyWithLength, String[]> sortedMap, int n) {
// Assuming map is not unmodifiable
if (n <= sortedMap.size()-1){
for (int i = 0; i< n;i++){
sortedMap.remove(sortedMap.firstKey());
}
}
}
}
class KeyWithLength implements Comparable<KeyWithLength> {
private String key;
private Integer length;
public KeyWithLength(String key, int length) {
super();
this.key = key;
this.length = length;
}
public String getKey() {
return key;
}
public int getLength() {
return length;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((key == null) ? 0 : key.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
KeyWithLength other = (KeyWithLength) obj;
if (key == null) {
if (other.key != null)
return false;
} else if (!key.equals(other.key))
return false;
return true;
}
#Override
public int compareTo(KeyWithLength another) {
// TODO Auto-generated method stub
return compare(this.length, another.length);
}
public static int compare(int x, int y) {
return (x < y) ? -1 : ((x == y) ? 0 : 1);
}
}
The output:
Original Map:
*** Entry 0:
A, B, D,
*** Entry 1:
1, 2, 3, 4,
*** Entry 2:
1, B, D, 1, B, D,
ShortenedMap:
*** Entry 0:
1, B, D, 1, B, D,

I can't figure out comparator

I'm trying to sort an arraylist but I can't wrap my head around the comparator. I don't understand how to define sortable fields from my arraylist which is created from a text file. Furthermore I'm unsure of the comparator logic. It seems to me like create a set of comparator functions, and then later invoke them. Is this true?
So far my code looks like this:
public class coord implements Comparator<Sort> {
private int index;
private int index2;
private double dista;
}
public class Sort {
List<Sort> coords = new ArrayList<Sort>();
public static void main(String[] args) throws Exception {
ArrayList dist = new ArrayList();
File file = new File("2.txt");
FileWriter writer = new FileWriter("2c.txt");
try {
Scanner scanner = new Scanner(file).useDelimiter("\\s+");
while (scanner.hasNextLine())
{
int index = scanner.nextInt();
int index2 = scanner.nextInt();
double dista = scanner.nextDouble();
System.out.println(index + " " + index2 + " " + dista);
}
}
}
public class EmpSort {
static final Comparator<coord> SENIORITY_ORDER =
new Comparator<coord>() {
public int compare(coord e1, coord e2) {
return e2.index().compareTo(e1.index());
}
};
static final Collection<coord> coords = ;
public static void main(String[] args) {
List<Sorted>e = new ArrayList<Sorted>(coords);
Collections.sort(e, SENIORITY_ORDER);
System.out.println(e);
I appreciate any help anyone can give.
Comparator logic is simple. When you sort an array of elements you have two choices - sort using the Comparable on each element (assuming there is one) or supply a Comparator. If your array contains complex elements or there are different sort criteria then the latter choice is probably what you need to use.
Each time the comparator is called you must say if element 1 is "less than" element 2 in which case return a negative number, element 1 is "greater than" element 3 in which case return a positive number. Otherwise if elements are equal return 0. You may also do reference and null comparison before comparing values so that null elements are logically "less than" non-null elements and so on.
If elements are "equal" then you may wish to sort by a secondary field and then a third field and keep going until the sort order is unambiguous.
A simple comparator for a class Complex which has fields a & b and we want to sort on a:
class Complex {
public String a = "";
public String b = "";
}
//...
Collections.sort(someList, new Comparator<Complex>() {
public int compare(Complex e1, Complex e2) {
if (e1 == e2) {
// Refs could be null or equal
return 0;
}
if (e1 == null && e2 != null) {
return -1;
}
if (e2 == null && e1 != null) {
return 1;
}
if (e1.a == e2.a) {
return 0;
}
if (e1.a == null && e2.a != null) {
return -1;
}
if (e1.a != null && e2.a == null) {
return 1;
}
// Just use the Comparable on the fields
return e1.a.compareTo(e2.a);
}
});

Categories