0

I am working on a school project than involves me making a program than takes a String an counts how many times all the words in the String appear. I have managed to do that, through making to arrays. One array is the String array and it holds the words and other Integer array that holds the number of times a word appear. A specific word and how many times it appears have the same element number in the two arrays.

But my problem is sorting them in decending order, I thought at first that I could do it by using this line of code:

 Arrays.sort(thenumbers, Collections.reverseOrder()); 

But I realised that this would sort the number of times a words appears but that would ruin the whole program because the words and the numbers would have different element number in the arrays.

Is there a way to fix this?

Any help would be appreciated, thanks!

AyhamSYR
  • 13
  • 4
  • 2
    The generally accepted method is to use a small (inner) class containing the number and word, put those in a list, and sort the list with either a custom `Comparator` or by making the class `Comparable`. One candidate for a class containing the number and word, without even writing it, is by using `AbstractMap.SimpleImmutableEntry`. – Mark Jeronimus Nov 13 '19 at 10:01
  • Or may be you could also use a HashMap. Of the word and its count? – Jatin Nov 13 '19 at 10:03
  • @Jatin. TreeMap is much better since it's actually a SortedMap – Mad Physicist Nov 13 '19 at 10:04
  • 1
    You're both wrong. Depending on how you order the generic parameters, it would either sort by the word, or discard words with the same number (same-key overwrite behavior) – Mark Jeronimus Nov 13 '19 at 10:05
  • @MarkJeronimus. Good call – Mad Physicist Nov 13 '19 at 10:06
  • Does this answer your question? [Sorting two arrays simultaneously](https://stackoverflow.com/questions/23339015/sorting-two-arrays-simultaneously) – Mad Physicist Nov 13 '19 at 10:09
  • I don't know if it's right to bring up an ancient question with an 'accepted' answer that's already outdated. – Mark Jeronimus Nov 13 '19 at 10:14

4 Answers4

0

If you want to do it with arrays only, without using Maps, so the word with count is not correlated in the same object, you can implement a custom bublesort that sorts the two arrays based on the count array.

For example:

static void bubbleSort(String[] words, int[] wordCount) {
    int n = wordCount.length;
    String wordTemp = null;
    int countTemp = 0;
    for (int i = 0; i < n; i++) {
        for (int j = 1; j < (n - i); j++) {
            if (wordCount[j - 1] < wordCount[j]) { // < for reverse order, > for ascend
                //swap words elements
                wordTemp = words[j-1];
                words[j-1] = words[j];
                words[j] = wordTemp;
                //swap wordcount elements
                countTemp = wordCount[j - 1];
                wordCount[j - 1] = wordCount[j];
                wordCount[j] = countTemp;
            }
        }
    }
}

It will apply a bublesort algorithm on the wordCount (which holds the occurrences of each word and at the same time of wordCount sorting, it will sort the words.

Running the following example as in

String [] words = {"four", "one", "five", "three", "six", "two"};
int [] wordCount = {4, 1, 5, 3, 6, 2};
bubbleSort(words, wordCount);
System.out.println("Words: " + Arrays.toString(words));
System.out.println("Word Count: " + Arrays.toString(wordCount));

Produces

Words: [six, five, four, three, two, one]
Word Count: [6, 5, 4, 3, 2, 1]
Ioannis Barakos
  • 1,319
  • 1
  • 11
  • 16
0

You don't have to make 2 separate arrays. Finding how many time each word is appeared in String has pretty standard solution with stream API:

import static java.util.stream.Collectors.*;

Map<String, Long> map = Arrays.stream(sourceString.split(" "))
        .collect(groupingBy(Function.identity(), counting()))

That gives you a map with word as a key and how many times it's appeared as a value. Then you could sort your map by value using any solution from Sort a Map<Key, Value> by values

Ruslan
  • 6,090
  • 1
  • 21
  • 36
0

The distinct non-answer: do a real OOP design.

What you are doing here is: you have data in two different places, and you manually "map" the corresponding items together via using the same index in those two arrays.

That is basically: procedural programming.

The OOP answer: create a class that represents a single word together with its occurrence count! And then you can implement the Comparable interface, and sort a list of such objects in one shot!

Meaning: the other answers are all technically correct, as they tell you how to solve your problem given your current design. But the real answer is: to replace your current design with something different, that will enable a much simpler solution!

GhostCat
  • 137,827
  • 25
  • 176
  • 248
0

I have to second this answer which suggests using an OOP design with a class encapsulating both, the string and count.

As a temporary solution, you can sort an array of indices instead of the array itself. Using a comparator which uses the index to extract the value of one array is similar to a comparator using a property of a dedicated type.

The difference to using dedicated objects is that you have to apply the indices to the original arrays to bring them into the desired order in a subsequent step:

String[] strings = { "foo", "bar", "baz" };
int[]    counts  = {     1,     3,     2 };

// create array of ascending indices
Integer[] indices = IntStream.range(0, strings.length).boxed().toArray(Integer[]::new);
// sort by counts, decending
Arrays.sort(indices, Comparator.comparing(ix -> counts[ix], Comparator.reverseOrder()));

// apply to counts
int[] newCounts = Arrays.stream(indices).mapToInt(ix -> counts[ix]).toArray();
// apply to strings
String[] newStrings = Arrays.stream(indices).map(ix -> strings[ix]).toArray(String[]::new);

System.out.println(Arrays.toString(newStrings));
System.out.println(Arrays.toString(newCounts));
[bar, baz, foo]
[3, 2, 1]
Holger
  • 285,553
  • 42
  • 434
  • 765