How to Output the Reverse of a Map [Java] - java

The problem goes as follows: Jane has friends who she associates a number with. I must output the friends from the least amount of likes to the most likes.
My main curiosity is how can I reverse the order of the map values when outputting.
In my code I had to extract the values through an Iterator (I was unable to use a Collection directly) and then store each String into an ArrayList by inserting each consecutive element at index 0. This, as a result, reversed the order which worked I suppose.
import java.util.*;
import java.io.*;
import static java.lang.System.*;
public class Friends {
public static void main(String args[]) throws IOException
{
Scanner line = new Scanner(new File("friends.dat"));
int trials = line.nextInt();
for(int k = 0 ; k < trials ; k++)
{
TreeMap<Integer, String> m = new TreeMap<Integer,String>();
int subtrials = line.nextInt();
for(int a = 0; a < subtrials ; a++)
{
String name = line.next();
int likes = line.nextInt();
m.put(likes,name);
}
Iterator iter = m.values().iterator(); //**Code of interest starts here**
ArrayList<String> list = new ArrayList<String>();
while(iter.hasNext()) {
list.add(0, (String)iter.next());
}
for(int a = 0 ; a < list.size() ; a++)
{
if(a == list.size() - 1)
out.print(list.get(a));
else
out.print(list.get(a) + ", ");
}
out.println();
}
}
}

You can use a custom comparator to reverse the order of entries in the map (note that this only applies to TreeMap - other map implementations don't care about ordering).
public static void main(String[] args) {
TreeMap<Integer, String> map = new TreeMap<>((key1, key2) -> Integer.compare(key2, key1)); //Custom comparator.
map.put(1, "Bob");
map.put(3, "Baz");
map.put(2, "Foo");
System.out.println(map);
}

Using the number of likes as a key seems strange, as multiple friends might have the same number of likes.
In Java 8, I'd do the following:
Map<String, Integer> map = new HashMap<>();
map.put("Jack", 7);
map.put("Jill", 3);
map.put("John", 12);
map.entrySet()
.stream()
.sorted(Map.Entry.comparingByValue((a, b) -> b.compareTo(a)))
.forEach(System.out::println);
Essentially, this turns the map entries into a stream, compares them by value using a comparator that reverses the natural order, and then prints out every entry.
Which results in:
John=12
Jack=7
Jill=3

Please try this:
m.forEach((a,b)->System.out.print(b+", "));
This will give you a sorted map, from the least amount of likes to the most likes.
If you want a sorted map from the most likes to the least amount of likes, you can do this:
TreeMap<Integer, String> m = new TreeMap<Integer,String>(Collections.reverseOrder());

You could simply reverse your list.
list.reverse();
Alternately, you could use the TreeMap constructor with a reverse comparator, to store the map in descending order.
... = new TreeMap<>(Collections.reverseOrder(Integer::compare));

Related

How to put List elements to String Array in Java

Is there a way to put list elements to a String array in java? I sorted a Map by value using a custom Comparator and now trying to put the key(String) elements into an array. The only way I found is looping through the array and the sorted list at the same time and fill up the array that way but it only puts the last element into it.
Example:
My map without sorting: {a=5, b=2, c=8, d=1}
After sorted with custom Comparator: [c=8, a=5, b=2, d=1]
Now I simply need to put the key values (c,a etc.) to the tuple final String[] lettersWithBigValues = new String[n] where n is the length of the tuple.
However, after:
for (int i = 0; i < Integer.parseInt(args[1]); i++) {
System.out.println(lettersWithBigValues[i]+",");
}
The console gives back:
d,d,d,d given that the console line argument is 4
Here is the full function:
public String[] getTopLettersWithValues(final int n){
final String[] lettersWithBigValues = new String[n];
final Map<String, Integer> myMap = new HashMap<>();
int counter = 0;
for (final List<String> record : records) {
if (!myMap.containsKey(record.get(1))) {
myMap.put(record.get(1), counter += Integer.parseInt(record.get(4)));
} else {
myMap.computeIfPresent(record.get(1), (k,v) -> v + Integer.parseInt(record.get(4)));
}
}
System.out.println(myMap);
List<Map.Entry<String, Integer>> sorted = new LinkedList<>(myMap.entrySet());
// Sort list with list.sort(), using a custom Comparator
sorted.sort(valueComparator);
System.out.println(sorted);
for (int i = 0; i < lettersWithBigValues.length; i++) {
for (Map.Entry<String, Integer> values: sorted) {
lettersWithBigValues[i] = values.getKey();
}
}
return lettersWithBigValues;
}
Where records is a List of data read from a csv file.
And here is the comparator:
public Comparator<Map.Entry<String, Integer>> valueComparator = (o1, o2) -> {
Integer v1 = o1.getValue();
Integer v2 = o2.getValue();
return v2.compareTo(v1);
};
You can attain the array of keys as follows:
String [] lettersWithBigValues = myMap.entrySet().stream() // entries of your intial map
.sorted(valueComparator) // sorted by your comparator
.map(Map.Entry::getKey) // mapped to only the key e.g. 'd', 'a'
.toArray(String[]::new); // converted to array
Seems to me that you have mistakenly used a nested for loop, you just need the one:
for (int i = 0; i < lettersWithBigValues.length; i++) {
lettersWithBigValues[i] = sorted.get(i).getKey();
}
That would return the array: c, a, b, d
As an added suggestion you could also have created the Comparator using the Entry.comparingByValue() method.
Comparator<Entry<String,Integer>> valueComparator =
Entry.<String,Integer>comparingByValue().reversed();
It returns a ready made Comparator that compares by the natural order of the value. So to sort in reverse, you just need to tag on the reversed() method which is defined in the Comparator interface.

Java ArrayList sort two lists in same order

I have two ArrayLists in Java. Both lists are unsorted.
ArrayList<Integer> listOne = new ArrayList<>();
listOne.add(2);
listOne.add(1);
listOne.add(4);
listOne.add(8);
listOne.add(6);
ArrayList<String> listTwo = new ArrayList<>();
listTwo.add("ant");
listTwo.add("bear");
listTwo.add("cat");
listTwo.add("dog");
listTwo.add("zebra");
I want to sort listOne in natural order and each item of listTwo should be sorted according to the position in listOne:
What I have so far is:
Collections.sort(listOne);
for (int i = 0; i < listOne.size(); i++) {
int intTest = listOne.get(i);
String stringTest = listTwo.get(i);
System.out.println(intTest);
System.out.println(stringTest);
}
This prints :
1 ant, 2 bear, 4 cat , 6 dog , 8 zebra
My expected print output is:
1 bear, 2 ant, 4 cat, 6 zebra, 8 dog
So that when the item of listOne "1", that changed the position from 2nd to 1st, the item "bear" in listTwo, that was on the 2nd position, should also print on the 1st position.
What would be the most simple and efficient way to do this?
Create an ordered list of indices:
int n = listOne.size();
assert n == listTwo.size();
Integer[] indices = new Integer[n];
for (int i = 0; i < n; ++i) {
indices[i] = i;
}
Sort that list using a comparator that compares indices by looking at the corresponding elements in listOne.
Arrays.sort(
indices,
new Comparator<Integer>() {
public int compare(Integer a, Integer b) {
return listOne.get(a).compareTo(listOne.get(b));
}
});
Now you can use indices to reorder both lists:
static <T> void reorder(Integer[] indices, List<T> mutatedInPlace) {
List<T> tempSpace = new ArrayList<T>(indices.length);
for (int index : indices) {
tempSpace.add(mutatedInPlace.get(index);
}
mutatedInPlace.clear();
mutatedInPlace.addAll(tempSpace);
}
reorder(indices, listOne);
reorder(indices, listTwo);
TreeMap best suits this situation. It inserts the data in sorted order so basically store the key and against each key you can store the animal.
Map<Integer,String> sortedMap = new TreeMap<Integer,String>();
sortedMap.push(listOne.get(i),listTwo.get(listOne.get(i)));
In case you want to stick to ArrayList, you can iterate over the listOne and push it in HashMap<Integer,String>
iterate over list (for example i)
map.put( listOne.get(i), secondList.get(i));
So the hashMap would be like (2, "ant");
Collections.sort(listOne);
Against each entry, you can get the corresponding animal from map
We can use HashMap data structure, It contains “key-value” pairs and allows retrieving the value by key.
int i = 0;
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
Here we’re storing the items from listOne as keys and their position as a value in the HashMap.
for (Integer num : listOne) {
map.put(num, i);
i++;
}
We’re printing the elements from listTwo as per the items from the listOne changed their position.
Collections.sort(listOne);
for (Integer num : listOne) {
System.out.println(num + " " + listTwo.get(map.get(num)));
}
One Solution:
int i = 0;
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
for (Integer num : listOne) {
map.put(num, i);
i++;
}
Collections.sort(listOne);
for (Integer num : listOne) {
System.out.println(num + " " + listTwo.get(map.get(num)));
}
Output is:
1 bear,
2 ant,
4 cat,
6 zebra,
8 dog
If you use a Map<Integer, String> for this, you don't even need to sort it if you take the TreeMap implementation.
It basically works as follows:
public class StackoverflowMain {
public static void main(String[] args) {
// initialize a map that takes numbers and relates Strings to the numbers
Map<Integer, String> animals = new TreeMap<Integer, String>();
// enter ("put" into the map) the values of your choice
animals.put(2, "ant");
animals.put(1, "bear");
animals.put(4, "cat");
animals.put(8, "dog");
animals.put(6, "zebra");
// print the whole map using a Java 8 forEach statement
animals.forEach((index, name) -> System.out.println(index + ": " + name));
}
}
This code will output
1: bear
2: ant
4: cat
6: zebra
8: dog
Putting the suggestions of all the friendly and helpful people together (plus some other research and test), here is the final code I came up with:
ArrayList<String> listOne = new ArrayList<>();
listOne.add("one");
listOne.add("eight");
listOne.add("three");
listOne.add("four");
listOne.add("two");
ArrayList<String> listTwo = new ArrayList<>();
listTwo.add("ant");
listTwo.add("bear");
listTwo.add("cat");
listTwo.add("dog");
listTwo.add("zebra");
Map<String, String> sortedMap = new TreeMap<String, String>();
for (int i = 0; i < listOne.size(); i++) {
String stringkey = listOne.get(i);
String stringValue = listTwo.get(i);
sortedMap.put(stringkey, stringValue);
}
print output = {eight=bear, four=dog, one=ant, three=cat, two=zebra}

How to split odd and even numbers and sum of both in a collection using Stream

How can I split odd and even numbers and sum both in a collection using stream methods of Java 8?
public class SplitAndSumOddEven {
public static void main(String[] args) {
// Read the input
try (Scanner scanner = new Scanner(System.in)) {
// Read the number of inputs needs to read.
int length = scanner.nextInt();
// Fillup the list of inputs
List<Integer> inputList = new ArrayList<>();
for (int i = 0; i < length; i++) {
inputList.add(scanner.nextInt());
}
// TODO:: operate on inputs and produce output as output map
Map<Boolean, Integer> oddAndEvenSums = inputList.stream(); // Here I want to split odd & even from that array and sum of both
// Do not modify below code. Print output from list
System.out.println(oddAndEvenSums);
}
}
}
You can use Collectors.partitioningBy which does exactly what you want:
Map<Boolean, Integer> result = inputList.stream().collect(
Collectors.partitioningBy(x -> x%2 == 0, Collectors.summingInt(Integer::intValue)));
The resulting map contains sum of even numbers in true key and sum of odd numbers in false key.
It's easiest (and cleanest) to do it in two separate stream operations, like such:
public class OddEvenSum {
public static void main(String[] args) {
List<Integer> lst = ...; // Get a list however you want, for example via scanner as you are.
// To test, you can use Arrays.asList(1,2,3,4,5)
Predicate<Integer> evenFunc = (a) -> a%2 == 0;
Predicate<Integer> oddFunc = evenFunc.negate();
int evenSum = lst.stream().filter(evenFunc).mapToInt((a) -> a).sum();
int oddSum = lst.stream().filter(oddFunc).mapToInt((a) -> a).sum();
Map<String, Integer> oddsAndEvenSumMap = new HashMap<>();
oddsAndEvenSumMap.put("EVEN", evenSum);
oddsAndEvenSumMap.put("ODD", oddSum);
System.out.println(oddsAndEvenSumMap);
}
}
One change I did make was making the resultant Map a Map<String,Integer> instead of Map<Boolean,Integer>. It's vey unclear what a key of true in the latter Map would represent, whereas string keys are slightly more effective. It's unclear why you need a map at all, but I'll assume that goes on to a later part of the problem.
List<Integer> numberList = Arrays.asList(1,3,4,60,12,22,333456,1239);
Map<Boolean, List<Integer>> evenOddNumbers = numberList.stream()
.collect(Collectors.partitioningBy( x -> x%2 == 0));
System.out.println(evenOddNumbers);
Output:
{false=[1, 3, 1239], true=[4, 60, 12, 22, 333456]}
and if want to separate it out the use below one:
List<Integer> evenNums = partitions.get(true);
List<Integer> oddNums = partitions.get(false);
Easiest way to find even numbers from list and then performing addition of even numbers using Stream :
int number = l.stream().filter(n->n%2==0).mapToInt(n -> n).sum();
System.out.println(number);

Syncronized sorting between two ArrayLists

I have two ArrayLists.
The first contains a group of words with capitalization and
punctuation.
The other contains this same group of words, but with the
capitalization and punctuation removed.
.
ArrayList1 ..... ArrayList2
MURDER! ........ murder
It's ........... its
Hello .......... hello
Yes-Man ........ yesman
ON ............. on
The second array has all the words alphabetized and all the letters in each word alphabetized. It looks something like this:
aemnsy
demrru
ehllo
ist
no
I want to make it so that when I arrange the words in ArrayList two into alphabetical order, all the words from ArrayList one follow suite:
ArrayList1 ..... ArrayList2
Yes-Man ........ aemnsy
MURDER! ........ demrru
Hello .......... ehllo
It's ........... ist
ON ............. no
I tried to make a loop with a for statement or two, but it ended up not working and became very long. How do I do this? How do I do this efficiently?
Here is a function to sort multiple lists based on a single 'key' list. The lists do not need to be the same type, here the key list is type String and it's used to sort a String, Integer, and Double list (Ideone Example):
List<String> key = Arrays.asList("demrru", "ist", "ehllo", "aemnsy", "no");
List<String> list1 = Arrays.asList("MURDER!","It's", "Hello","Yes-Man", "ON");
List<Integer> list2 = Arrays.asList(2, 4, 3, 1, 5); // Also use Integer type
List<Double> list3 = Arrays.asList(0.2, 0.4, 0.3, 0.1, 0.5); // or Double type
// Sort all lists (excluding the key)
keySort(key, list1, list2, list3);
// Sort all lists (including the key)
keySort(key, key, list1, list2, list3);
Output:
// Sorted by key:
[Yes-Man, MURDER!, Hello, It's, ON]
[aemnsy, demrru, ehllo, ist, no]
[1, 2, 3, 4, 5]
[0.1, 0.2, 0.3, 0.4, 0.5]
Sort Function
An Ideone Example can be found here which includes validation of parameters and a test case.
public static <T extends Comparable<T>> void keySort(
final List<T> key, List<?>... lists){
// Create a List of indices
List<Integer> indices = new ArrayList<Integer>();
for(int i = 0; i < key.size(); i++)
indices.add(i);
// Sort the indices list based on the key
Collections.sort(indices, new Comparator<Integer>(){
#Override public int compare(Integer i, Integer j) {
return key.get(i).compareTo(key.get(j));
}
});
// Create a mapping that allows sorting of the List by N swaps.
Map<Integer,Integer> swapMap = new HashMap<Integer, Integer>(indices.size());
// Only swaps can be used b/c we cannot create a new List of type <?>
for(int i = 0; i < indices.size(); i++){
int k = indices.get(i);
while(swapMap.containsKey(k))
k = swapMap.get(k);
swapMap.put(i, k);
}
// for each list, swap elements to sort according to key list
for(Map.Entry<Integer, Integer> e : swapMap.entrySet())
for(List<?> list : lists)
Collections.swap(list, e.getKey(), e.getValue());
}
First Way - I use map whose key is from arrayList2 and value is from arrayList1. Putting data to map is up to you. After sorting arrayList2, I get its value from map.
List<String> arrList1 = new ArrayList<String>();
arrList1.add("MURDER!");
arrList1.add("It's");
arrList1.add("Hello");
arrList1.add("Yes-Man");
arrList1.add("ON");
List<String> arrList2 = new ArrayList<String>();
arrList2.add("demrru");
arrList2.add("aemnsy");
arrList2.add("ist");
arrList2.add("ehllo");
arrList2.add("no");
Map<String, String> map1 = new HashMap<String, String>();
map1.put("aemnsy", "Yes-Man");
map1.put("demrru", "MURDER!");
map1.put("ehllo", "Hello");
map1.put("ist", "It's");
map1.put("no", "ON");
Collections.sort(arrList2);
for (String s : arrList2){
System.out.println(s + "..........." + map1.get(s));
}
Second Way - Another way is you can use only TreeMap which is already sorted instead of two ArrayList.
Map<String, String> map2 = new TreeMap<String, String>();
map2.put("ehllo", "Hello");
map2.put("aemnsy", "Yes-Man");
map2.put("demrru", "MURDER!");
map2.put("no", "ON");
map2.put("ist", "It's");
for (Map.Entry<String, String> entry : map2.entrySet())
{
System.out.println(entry.getKey() + "/" + entry.getValue());
}
Third way - only using 2 ArrayList, but we have to write sorting method by our own. Have you notice that your 2 ArrayList elements such as aemnsy from arrayList2 and Yes-Man from arrayList1 have same index? I use that point.
selectionSort1(arrList2, arrList1);
for(int i = 0; i < arrList1.size(); i++){
System.out.println(arrList2.get(i) + "---" + arrList1.get(i));
}
public static void selectionSort1(List<String> x, List<String> y) {
for (int i=0; i<x.size()-1; i++) {
for (int j=i+1; j<x.size(); j++) {
if (x.get(i).compareTo(x.get(j)) > 0) {
//... Exchange elements in first array
String temp = x.get(i);
x.set(i, x.get(j));
x.set(j, temp);
//... Exchange elements in second array
temp = y.get(i);
y.set(i, y.get(j));
y.set(j, temp);
}
}
}
}
A Quick Answer.
public class MapAlph {
public static void main(String[] args) {
HashMap<String, String> map = new HashMap<String, String>();
String txt = "Murde!r!";
ArrayList<Character> alph = new ArrayList<Character>();
for (int i = 0; i < txt.length(); i++)
if (Character.isLetter(txt.charAt(i)))
alph.add(txt.charAt(i));
Collections.sort(alph);
Collections.reverse(alph);
String val = "";
for (Character c : alph)
val += c;
map.put(txt, val);
System.out.print(txt + " ........ " + map.get(txt));
}
}
You can make use of TreeMap, if you wish.
Map<String, String> sortedMap = new TreeMap<String, String>();
sortedMap.put("demrru", "MURDER!");
sortedMap.put("ist", "It's");
sortedMap.put("aemnsy", "Yes-Man");
sortedMap.put("ehllo", "Hello");
sortedMap.put("no", "ON");
You can try this:
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
public class SortService {
public static void main(String[] args) {
Map<String, String> originalMap = new HashMap<String, String>();
originalMap.put("aemnsy", "Yes-Man");
originalMap.put("demrru", "MURDER!");
originalMap.put("ehllo", "Hello");
originalMap.put("ist", "It's");
originalMap.put("no", "ON");
Map<String, String> sortedMap = new TreeMap<String, String>(originalMap);
System.out.println(sortedMap);
}
}
output:
{aemnsy=Yes-Man, demrru=MURDER!, ehllo=Hello, ist=It's, no=ON}

Sorting words in order of frequency? (least to greatest)

does any one have any idea how to sort a list of words in the order of their frequency (least to greatest) using the built in collection.sort and a comparator<string> interface?
I already have a method that gets the count of a certain word in the text file. Now, I just need to create a method that compares the counts of each word and then puts them in a list sorted by the least frequency to the greatest.
Any ideas and tips would be very much appreciated. I'm having trouble getting started on this particular method.
public class Parser implements Comparator<String> {
public Map<String, Integer> wordCount;
void parse(String filename) throws IOException {
File file = new File(filename);
Scanner scanner = new Scanner(file);
//mapping of string -> integer (word -> frequency)
Map<String, Integer> wordCount = new HashMap<String, Integer>();
//iterates through each word in the text file
while(scanner.hasNext()) {
String word = scanner.next();
if (scanner.next()==null) {
wordCount.put(word, 1);
}
else {
wordCount.put(word, wordCount.get(word) + 1);;
}
}
scanner.next().replaceAll("[^A-Za-z0-9]"," ");
scanner.next().toLowerCase();
}
public int getCount(String word) {
return wordCount.get(word);
}
public int compare(String w1, String w2) {
return getCount(w1) - getCount(w2);
}
//this method should return a list of words in order of frequency from least to greatest
public List<String> getWordsInOrderOfFrequency() {
List<Integer> wordsByCount = new ArrayList<Integer>(wordCount.values());
//this part is unfinished.. the part i'm having trouble sorting the word frequencies
List<String> result = new ArrayList<String>();
}
}
First of all your usage of scanner.next() seems incorrect. next() will return the next word and move onto next one every time you call it, therefore the following code:
if(scanner.next() == null){ ... }
and also
scanner.next().replaceAll("[^A-Za-z0-9]"," ");
scanner.next().toLowerCase();
will consume and then just throw away words. What you probably want to do is:
String word = scanner.next().replaceAll("[^A-Za-z0-9]"," ").toLowerCase();
at the beginning of your while loop, so that the changes to your word are saved in the word variable, and not just thrown away.
Secondly, the usage of the wordCount map is slightly broken. What you want to do is to check if the word is already in the map to decide what word count to set. To do this, instead of checking for scanner.next() == null you should look in the map, for example:
if(!wordCount.containsKey(word)){
//no count registered for the word yet
wordCount.put(word, 1);
}else{
wordCount.put(word, wordCount.get(word) + 1);
}
alternatively you can do this:
Integer count = wordCount.get(word);
if(count == null){
//no count registered for the word yet
wordCount.put(word, 1);
}else{
wordCount.put(word, count+1);
}
I would prefer this approach, because it's a bit cleaner, and does only one map look-up per word, whereas the first approach sometimes does two look-ups.
Now, to get a list of words in descending order of frequencies, you can convert your map to a list first, then apply Collections.sort() as was suggested in this post. Below is a simplified version suited to your needs:
static List<String> getWordInDescendingFreqOrder(Map<String, Integer> wordCount) {
// Convert map to list of <String,Integer> entries
List<Map.Entry<String, Integer>> list =
new ArrayList<Map.Entry<String, Integer>>(wordCount.entrySet());
// Sort list by integer values
Collections.sort(list, new Comparator<Map.Entry<String, Integer>>() {
public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
// compare o2 to o1, instead of o1 to o2, to get descending freq. order
return (o2.getValue()).compareTo(o1.getValue());
}
});
// Populate the result into a list
List<String> result = new ArrayList<String>();
for (Map.Entry<String, Integer> entry : list) {
result.add(entry.getKey());
}
return result;
}
Hope this helps.
Edit:
Changed the comparison function as suggested by #dragon66. Thanks.
You can compare and extract ideas from the following:
public class FrequencyCount {
public static void main(String[] args) {
// read in the words as an array
String s = StdIn.readAll();
// s = s.toLowerCase();
// s = s.replaceAll("[\",!.:;?()']", "");
String[] words = s.split("\\s+");
// sort the words
Merge.sort(words);
// tabulate frequencies of each word
Counter[] zipf = new Counter[words.length];
int M = 0; // number of distinct words
for (int i = 0; i < words.length; i++) {
if (i == 0 || !words[i].equals(words[i-1])) // short-circuiting OR
zipf[M++] = new Counter(words[i], words.length);
zipf[M-1].increment();
}
// sort by frequency and print
Merge.sort(zipf, 0, M); // sorting a subarray
for (int j = M-1; j >= 0; j--) {
StdOut.println(zipf[j]);
}
}
}
A solution, close to your original posting with corrections and the sorting as suggested by Torious in the comments:
import java.util.*;
public class Parser implements Comparator <String> {
public Map<String, Integer> wordCount;
void parse ()
{
Scanner scanner = new Scanner (System.in);
// don't redeclare it here - your attribute wordCount will else be shadowed
wordCount = new HashMap<String, Integer> ();
//iterates through each word in the text file
while (scanner.hasNext ()) {
String word = scanner.next ();
// operate on the word, not on next and next of next word from Scanner
word = word.replaceAll (" [^A-Za-z0-9]", " ");
word = word.toLowerCase ();
// look into your map:
if (! wordCount.containsKey (word))
wordCount.put (word, 1);
else
wordCount.put (word, wordCount.get (word) + 1);;
}
}
public int getCount (String word) {
return wordCount.get (word);
}
public int compare (String w1, String w2) {
return getCount (w1) - getCount (w2);
}
public List<String> getWordsInOrderOfFrequency () {
List<String> justWords = new ArrayList<String> (wordCount.keySet());
Collections.sort (justWords, this);
return justWords;
}
public static void main (String args []) {
Parser p = new Parser ();
p.parse ();
List<String> ls = p.getWordsInOrderOfFrequency ();
for (String s: ls)
System.out.println (s);
}
}
rodions Solution is a kind of a Generics hell, but I don't have it simpler - just different.
In the End, his solution is shorter and better.
At the first looks, it seems that a TreeMap might be appropriate, but it sorts by Key, and is of no help for sorting by value, and we can't switch key-value, because we look it up by the key.
So the next idea is to generate a HashMap, and use Collections.sort, but it doesn't take a Map, just Lists for sorting. From a Map, there is entrySet, which produces another Collection, which is a Set, and not a List. That was the point where I took another direction:
I implemented an Iterator: I iterate over the entrySet, and only return Keys, where the value is 1. If the value is 2, I buffer them for later use. If the Iterator is exhausted, I look into the buffer, and if it isn't empty, I use the iterator of the buffer in future, increment the minimum value I look for, and create a new Buffer.
The advantage of an Iterator/Iterable pair is, that the values can be obtained by the simplified for-loop.
import java.util.*;
// a short little declaration :)
public class WordFreq implements Iterator <Map.Entry <String, Integer>>, Iterable <Map.Entry <String, Integer>>
{
private Map <String, Integer> counter;
private Iterator <Map.Entry <String, Integer>> it;
private Set <Map.Entry <String, Integer>> buf;
private int maxCount = 1;
public Iterator <Map.Entry <String, Integer>> iterator () {
return this;
}
// The iterator interface expects a "remove ()" - nobody knows why
public void remove ()
{
if (hasNext ())
next ();
}
public boolean hasNext ()
{
return it.hasNext () || ! buf.isEmpty ();
}
public Map.Entry <String, Integer> next ()
{
while (it.hasNext ()) {
Map.Entry <String, Integer> mesi = it.next ();
if (mesi.getValue () == maxCount)
return mesi;
else
buf.add (mesi);
}
if (buf.isEmpty ())
return null;
++maxCount;
it = buf.iterator ();
buf = new HashSet <Map.Entry <String, Integer>> ();
return next ();
}
public WordFreq ()
{
it = fill ();
buf = new HashSet <Map.Entry <String, Integer>> ();
// The "this" here has to be an Iterable to make the foreach work
for (Map.Entry <String, Integer> mesi : this)
{
System.out.println (mesi.getValue () + ":\t" + mesi.getKey ());
}
}
public Iterator <Map.Entry <String, Integer>> fill ()
{
counter = new HashMap <String, Integer> ();
Scanner sc = new Scanner (System.in);
while (sc.hasNext ())
{
push (sc.next ());
}
Set <Map.Entry <String, Integer>> set = counter.entrySet ();
return set.iterator ();
}
public void push (String word)
{
Integer i = counter.get (word);
int n = 1 + ((i != null) ? i : 0);
counter.put (word, n);
}
public static void main (String args[])
{
new WordFreq ();
}
}
Since my solution reads from stdin, you invoke it with:
cat WordFreq.java | java WordFreq

Categories

Resources