0

Currently I have 2 array lists showing words and their frequency. So "the" has a frequency of 1, "I" has a frequency of 10 and so on.

  ArrayList<String> words = new ArrayList<>(Arrays.asList("the", "I", "false","too"));
        
  ArrayList<Integer> frequency = new ArrayList<>(Arrays.asList(1, 10, 5, 7));


  Collections.sort(frequency, Collections.reverseOrder());

What I want to do is sort them from highest word frequency to lowest, so I used Collections.sort to sort frequency from highest values to lowest. This gives me the expected result of

[10, 7, 5, 1]

But now I'm at a complete standstill has to how to sort the words ArrayList so that the indexes of each list still correspond to each other.

My desired output would be for the words arraylist would be.

["I", "too", "false", "the"]

Is there some kind of method within Collections that can accomplish this?

2 Answers 2

2

You can sort the indices [0, 1, 2, 3] by frequency.get(i) and map the results with words.get(i) respectively.

ArrayList<String> words = new ArrayList<>(Arrays.asList("the", "I", "false","too"));
ArrayList<Integer> frequency = new ArrayList<>(Arrays.asList(1, 10, 5, 7));

List<String> sortedWords = IntStream.range(0, words.size())
    .boxed()
    .sorted(Collections.reverseOrder(Comparator.comparing(frequency::get)))
    .map(words::get)
    .collect(Collectors.toList());

System.out.println(sortedWords);

output:

[I, too, false, the]
Sign up to request clarification or add additional context in comments.

Comments

0

The task is quite trivial, if you use the correct data structure for it. For this particular task it is java.util.Map. Maps contain key-value pairs (mappings, entries). Then it becomes easy to sort words by frequency, without losing the mapping word -> frequency.

public class Test {

  public static void main(String[] args) {
    ArrayList<String> words = new ArrayList<>(Arrays.asList("the", "I", "false","too"));
    ArrayList<Integer> frequency = new ArrayList<>(Arrays.asList(1, 10, 5, 7));

    Map<String, Integer> wordFrequency = new HashMap<>();
    for (int i = 0; i < words.size(); i++) {
      wordFrequency.put(words.get(i), frequency.get(i));
    }
    List<String> sortedByFrequency = wordFrequency.entrySet()
            .stream()
            .sorted((e1, e2) -> Integer.compare(e2.getValue(), e1.getValue()))
            .map(Map.Entry::getKey)
            .collect(Collectors.toList());
    System.out.println(sortedByFrequency);
  }
}

Depending on how you populate the lists initially, you can directly populate the Map and not use the lists at all.

Map<String, Integer> wordFrequency = new HashMap<>();
wordFrequency.put("the", 1);
wordFrequency.put("I", 10);
//and so on

Another option would be to use intermediate object to contain the mapping word -> frequency.

public class Pair {

  private final String word;
  private final int frequency;

  //constructor
  //getters
}

Then map values in the list to this intermediate object, sort by frequency, extract words, collect.

List<String> sortedByFrequency = IntStream.range(0, words.size())
        .mapToObj(i -> new Pair(words.get(i), frequency.get(i)))
        .sorted(Comparator.comparing(Pair::getFrequency).reversed())
        .map(Pair::getWord)
        .collect(Collectors.toList());
System.out.println(sortedByFrequency);

If you are using java 14 or above you can make Pair a record to reduce boilerplate.

public record Pair(String word, int frequency) {
}
//same as example above
    .sorted(Comparator.comparing(Pair::frequency).reversed())
    .map(Pair::word)
//same as example above

1 Comment

I’d suggest making Pair into a record.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.