0

I have some list of words List<String> strings I have to make a word out of most frequent letters on each position.

For example for strings = {"qwerty", "qwer" , "ab", "bab"} the result would be "qwer"

Letters that appear once do not count

I came up to idea of creating a list

List<Map<String, Integer>> list;

Its size is equal to the string in strings of maximal size and the map would count number of characters on the position

IntStream.range(0, strings.stream().max(String::compareTo).get().length()) 
    .forEachOrdered(i -> {
        strings.stream().forEach(s -> {
            if (s.length()>i){
                Map<String,Integer> temp = list.get(i);
                String character = s.charAt(i) + "";
                Integer value = temp.get(character) == null ? 0 : temp.get(character) + 1;
                temp.put(character, value);
                list.add(i, temp);
            }
        });
    });

Have no idea why id doesn't work

6
  • But in your example strings is an array. Commented Apr 1, 2017 at 15:51
  • 1
    I don't get your example. Shouldn't the result be "qwerty" since 't' and 'y' are the most frequent characters for positions 5,6? Commented Apr 1, 2017 at 15:53
  • You need to provide further examples that help clarify what exactly the problem is Commented Apr 1, 2017 at 16:11
  • What about "ab". They appear more than once. Commented Apr 1, 2017 at 18:46
  • @NickZiebert: He wants a string comprised from the most frequent character at each position. There are two 'q' characters at position one. Therefore first character of the result should be q. Commented Apr 1, 2017 at 19:04

2 Answers 2

4

Here's another approach:

import java.util.AbstractMap.SimpleEntry;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.IntStream;

import static java.util.function.Function.identity;
import static java.util.stream.Collectors.*;

...

strings.stream()
    .flatMap(s -> IntStream.range(0, s.length()).mapToObj(i -> new SimpleEntry<>(i, s.charAt(i))))
    .collect(groupingBy(SimpleEntry::getKey, mapping(SimpleEntry::getValue, groupingBy(identity(), counting()))))
    .entrySet()
    .stream()
    .map(e -> e.getValue().entrySet().stream().max(Map.Entry.comparingByValue()).get())
    .filter(e -> e.getValue() > 1)
    .forEach(System.out::println);

which outputs:

q=2
w=2
e=2
r=2

From the stream of strings you create a stream of entries for each character mapped with their respective positions. From there you group them by the position and you group the values so that for each position you have a Map representing the occurences of the different characters at this position.

Then you filter out the values that have a count less than 2 (your 'Letters that appear once do not count' requirement)and finally you get the most frequent letter for each remaining position.

Sign up to request clarification or add additional context in comments.

18 Comments

@NickZiebert Yes, forgot to change the Collections.max part, will update
@NickZiebert It should be working now, thanks for the comment.
@jrook Ok, I will update it to not take any value in case of a tie. By the way if the downvoter can explain why that would be helpful for improving the solution.
@jrook Ok, I misunderstood the 'Letters that appear once do not count' requirement. I thought that it would mean that you should take the second max length of all the strings and do the stuff from there. Now, I hope it should work.
@NickZiebert Yes, I think misunderstood one requirement (see above comment). The update should work, hopefully.
|
1

Here's my take. For maximum length string, I used the formula given here. I am assuming your string has only alphabetical characters in it. Therefore there is no need for a hashmap. An array of size 26 will do. Then I just loop over each position and find the most frequent character.

public class WordsWithMostFrequentChars {
public static void main(String[] args) {
    String[] st = new String[] { "qwerty", "qwer" , "ab", "bab"};
    List<String> strings = Arrays.asList(st);
    List<Character> result = new ArrayList<Character>();
    int maxLength = strings.stream().max(Comparator.comparingInt(String::length)).get().length();

    IntStream.range(0,maxLength).forEach(i -> {
        int[] freq = new int[26];
        strings.stream().forEach(s -> {
            Character c;
            if(s.length()>i) {
                c=s.charAt(i);
                freq[c - 'a'] = freq[c - 'a'] + 1;
            }
        });
        // max finder
        int max = 0;
        for (int k = 1; k < 26; k++)
            if (freq[k] > freq[max])
                max = k;

        if(freq[max]>1) result.add((char) ('a' + max));

        });

        System.out.println(result);

    }
}

Run:

[q, w, e, r]

6 Comments

The main diddiculty wan in usnig ONLY stream api
If the only limitation is using only Java 8 stream api, then just replace the for loops above with forEach. It should be a trivial thing to do. For max finding there are also a lot of Java 8 solutions available on SO. You should try and ask your question more clearly and specify exactly what you want.
I thought the answer was qwer not qwerty
If you order by the same property, you will finally query, it’s much simpler and more efficient, to map to the property first, i.e. instead of strings.stream().max(Comparator.comparingInt(String::length)) .get().length(), you can use strings.stream().mapToInt(String::length).max().orElse(0)
By the way, a StringBuilder makes a much better result type than an ArrayList<Character>()
|

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.