Bartek
Bartek

Reputation: 2219

Most frequent element stream

How to find most frequent element, but when there are few most frequent element return null.

I would like to find code equivalent of:

public static void main(String[] args) {
    System.out.println("Should return A -> " + mostFrequent(Arrays.asList("A", "A", "B")));
    System.out.println("Should null as element in list have same frequency -> "
            + mostFrequent(Arrays.asList("A", "B")));
}

private static String mostFrequent(List<String> elements) {
    Map<String, Long> ordered = new TreeMap<>();
    for (String e : elements) {
        if (!ordered.containsKey(e)) {
            ordered.put(e, 0L);
        }
        Long tmp = ordered.get(e);
        ordered.put(e, ++tmp);
    }

    String mostFrequent = null;
    long i = 0;
    Iterator<Map.Entry<String, Long>> it = ordered.entrySet().iterator();
    while (it.hasNext() && i < 2) {
        Map.Entry<String, Long> pair = it.next();
        if (i == 0) {
            mostFrequent = pair.getKey();
        } else {
            if (ordered.get(mostFrequent) == ordered.get(pair.getKey())) {
                return null;
            }
        }
        i++;
    }

    return mostFrequent;
}

However stream version does not handle most frequent elements with the same frequency.

private static String mostFrequentStream(List<String> elements) {
    return elements.stream()
            .reduce(BinaryOperator.maxBy(
                    Comparator.comparingInt(o -> Collections.frequency(elements, o))))
            .orElse(null);
}

How to modify stream above to achieve it?

Upvotes: 0

Views: 3397

Answers (3)

LuCio
LuCio

Reputation: 5183

I managed to build a concatenated Stream but it got long:

private static String mostFrequentStream3(List<String> elements) {
    return elements.stream() // part 1
            .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
            .entrySet().stream() // part 2
            .collect(Collectors.groupingBy(Entry::getValue))
            .entrySet().stream() // part 3
            .max(Entry.comparingByKey())
            .map(Entry::getValue)
            .filter(v -> v.size() == 1)
            .map(v -> v.get(0).getKey())
            .orElse(null);
}

To "find most frequent element, but when there are few most frequent element return null"
Part 1 counts the frequency of every element.
Part 2 groups entries by frequency.
Part 3 looks up the entry with the highest frequency. If this entry does only have one element ("there are few most frequent"), then it's the one and only maximum. Otherwise null is returned.

Upvotes: 2

Ousmane D.
Ousmane D.

Reputation: 56453

using groupingBy:

String mostFrequentStream(List<String> elements) {
    Map<String, Long> temp = elements.stream()
            .collect(Collectors.groupingBy(a -> a, Collectors.counting()));


    return new HashSet<>(temp.values()).size() < temp.size() ? 
          null : temp.entrySet()
                     .stream()
                     .max(Map.Entry.comparingByValue())
                     .map(Map.Entry::getKey).get();

}

Upvotes: 4

shakhawat
shakhawat

Reputation: 2727

I would never use stream for this to avoid hurting readability and performance at the same time. For the sake of fun -

private static String mostFrequentStream(List<String> elements) {
    Map<String, Long> frequencyMap = elements.stream().collect(groupingBy(Function.identity(), counting()));

    return frequencyMap.entrySet().stream()
            .sorted(Map.Entry.<String, Long>comparingByValue().reversed())
            .limit(2).reduce((i, e) -> i.getValue().equals(e.getValue()) ? new AbstractMap.SimpleEntry<>(null, 0L) : i).get().getKey();
}

Upvotes: -1

Related Questions