Can you please give me advice on how to print word occurrences from the most frequent value to the least frequent?
I've tried different methods, so I stopped on the Map, it gives me a much closer result.
public class InputOutput {
private String wordsFrequency() {
StringBuilder result = new StringBuilder();
try {
Map<String, Integer> map = new HashMap<>();
BufferedReader reader = new BufferedReader(new FileReader("words.txt"));
String words;
while ((words = reader.readLine()) != null) {
Scanner scan = new Scanner(words);
while (scan.hasNext()) {
String word = scan.next();
if (map.containsKey(word))
map.put(word, map.get(word) 1);
else
map.put(word, 1);
}
scan.close();
}
reader.close();
Set<Entry<String, Integer>> entrySet = map.entrySet();
for (Entry<String, Integer> entry : entrySet) {
result.append(entry.getKey()).append("\t").append(entry.getValue()).append("\n");
}
} catch (IOException e) {
e.printStackTrace();
}
return result.toString();
}
public static void main(String[] args) {
InputOutput requestedData = new InputOutput();
System.out.println(requestedData.wordsFrequency());
}
}
File contents:
the day is sunny the the
the sunny is is is is is is
Expected output:
is 7
the 4
sunny 2
day 1
The output I'm getting:
the 4
is 7
sunny 2
day 1
CodePudding user response:
List<Map.Entry<String, Integer>> frequencies = new ArrayList<>(map.entrySet());
frequencies.sort(Comparator.comparing(e -> e.getValue()).reversed());
A List may be sorted, or a TreeSet can be sorted (SortedSet) using a Comparator. Here with a function returning a Comparable value.
CodePudding user response:
I'm sure there's a cleaner way to do it, but without using streams here's what I came up with:
String src = "the day is sunny the the the sunny is is is is is is";
try (Scanner scanner = new Scanner(new StringReader(src))) {
Map<String, Integer> map = new HashMap<>();
while (scanner.hasNext()) {
String word = scanner.next();
map.merge(word, 1, (a, b) -> a 1);
}
Map<Integer, Collection<String>> cntMap = new TreeMap<>(Comparators.reverseOrder());
for (Entry<String, Integer> entry : map.entrySet()) {
Collection<String> list = cntMap.get(entry.getValue());
if (list == null) {
list = new TreeSet<>();
cntMap.put(entry.getValue(), list);
}
list.add(entry.getKey());
}
for (Entry<Integer, Collection<String>> entry : cntMap.entrySet()) {
System.out.println(entry.getValue() " : " entry.getKey());
}
}
CodePudding user response:
The issues with the code you've provided:
In case of the exception, the stream would not be closed. More over ever if all the data would be successfully read from a file, but exception occur during closing the reader you'll the data because lines of code that are responsible for processing the map will not be executed. Use try with resources to ensure that your resources would be properly closed.
Don't cram too much logic into one method. There are at least two responsibilities, and they should reside in separate methods, as the Single responsibility principle suggests.
Instead of utilizing
Scanner
you can split the line that has been read from a file.And your current logic lucks sorting. That's why your current and expected output don't match.
You can generate a map Map<String, Integer>
representing the frequency of each word.
Then create a list of entries of this map, sort it based on values in descending order.
And finally turn the sorted list of entries into a list of strings which you can print.
private static Map<String, Integer> wordsFrequency(String file) {
Map<String, Integer> frequencies = new HashMap<>();
try (var reader = Files.newBufferedReader(Path.of(file))) {
String[] words = reader.readLine().split(" ");
for (String word : words) {
// frequencies.merge(word, 1, Integer::sum); // an equivalent of the 2 lines below
int count = frequencies.getOrDefault(word, 0);
frequencies.put(word, count 1);
}
} catch (IOException e) {
e.printStackTrace();
}
return frequencies;
}
public static List<String> mapToSortedList(Map<String, Integer> map) {
List<Map.Entry<String, Integer>> entries = new ArrayList<>(map.entrySet());
// sorting the list of entries
entries.sort(Map.Entry.<String, Integer>comparingByValue().reversed());
List<String> result = new ArrayList<>();
for (Map.Entry<String, Integer> entry :entries) {
result.add(entry.getKey() " " entry.getValue());
}
return result;
}
public static void main(String[] args) {
mapToSortedList(wordsFrequency("filePath.txt")).forEach(System.out::println);
}
CodePudding user response:
You already have your data, here is how to get them in reverse sorted order.
- declare a
SortedSet
using the comparator to compare the the values of the entries - then add the entries to the
SortedSet
and the will be sorted as they are entered. Entry.comparingByValue(Comparator.reversed())
is used to sort on the count only and in reversed order.
SortedSet<Entry<String,Integer>> set
= new TreeSet(Entry.comparingByValue(Comparator.reverseOrder()));
set.addAll(map.entrySet());
Then print them.
set.forEach(e-> System.out.printf("%-7s : %d%n", e.getKey(), e.getValue()));
For your data, this would print
is : 7
the : 4
sunny : 2
day : 1