Home > Mobile >  Join element into a list and map the list to hashmap
Join element into a list and map the list to hashmap

Time:10-12

I have made the following code:

Stream
    .concat(
        _question.getIncorrectAnswers().stream(), 
        Stream.of(_question.getCorrectAnswer())
    )
    .collect(Collectors.collectingAndThen(
        Collectors.toList(), 
        collected -> {
            Collections.shuffle(collected);
            return collected.stream();
        }
    ))
    .collect(Collectors.toMap(index  , Object::toString));

What I am trying to achieve is to join _question.getCorrectAnswer() which is a String object into _question.getIncorrectAnswers() which is a List of Strings.

I then want to shuffle the list I made and then map the list into this:

private final Map<Integer, String> _options = new HashMap<>();

which is a map that contains a counter (starting from 1) and the String the list contains.

I want do this using Java Streams into 1 line (for education purpose mostly).

I know how to make it using 3-4 lines but I am seeking for a more complex way so I can understand and learn new methods.

Any help or guidance is appreciated.

CodePudding user response:

The index comes with compilation error, fix it with an incremental object.

AtomicInteger index = new AtomicInteger();
Object v = Stream.concat(_question.getIncorrectAnswers().stream(), Stream.of(_question.getCorrectAnswer())).collect(Collectors.collectingAndThen(Collectors.toList(), collected ->
{
    Collections.shuffle(collected);
    return collected.stream();
})).collect(Collectors.toMap(i -> index.incrementAndGet(), Object::toString));

CodePudding user response:

Here is one approach using java.util.Random:

Assuming you have this two methods returning a list and a string:

static List<String> getIncorrectAnswers(){
    return List.of("bar", "baz", "doo");
}

static String getCorrectAnswer() {
    return "foo";
}

generate random ints between 0 and getIncorrectAnswers().size() 1, map each random int i to a string (if i == getIncorrectAnswers().size() then to correct answer else to incorrect answr at index i) finally collect to map using

collect(Supplier<R> supplier, 
        BiConsumer<R, ? super T> accumulator, 
        BiConsumer<R, R> combiner)

Example:

public static void main(String[] args) {
    Random random = new Random();
    Map<Integer,String> result =
    random.ints(0, getIncorrectAnswers().size() 1)
            .distinct()
            .limit(getIncorrectAnswers().size() 1)
            .mapToObj(i -> i == getIncorrectAnswers().size() ? getCorrectAnswer() : getIncorrectAnswers().get(i))
            .collect(HashMap::new, (m,s) -> m.put(m.size()   1, s), (m1,m2) -> {
                        int offset = m1.size();
                        m2.forEach((i,s) -> m1.put(i   offset, s));}
            );

    result.entrySet().forEach(System.out::println);
}

CodePudding user response:

It may be possible to use Stream::sorted with a custom "comparator" to randomize the order instead of Collections.shuffle.

Also, when calculating the key, an AtomicInteger k or int[] k should be used to increment the key.

In the following code sample the entries of the concatenated string are randomly sorted:

public static Map<Integer, String> buildOptions(Question question) {
    AtomicInteger k = new AtomicInteger(1);
    return Stream.concat(Stream.of(question.getCorrectAnswer()),
                         question.getIncorrectAnswers().stream())
                 .sorted((s1, s2) -> ThreadLocalRandom.current().nextInt(7) - 3)
                 .collect(Collectors.toMap(x -> k.getAndIncrement(), x -> x));
}

Or a sequence of indexes may be generated and sorted randomly (boxing is needed because IntStream does not have sorted with a custom comparator):

public static Map<Integer, String> buildOptionsIndex(Question question) {
    int[] k = {1};
    int n = question.getIncorrectAnswers().size();
    return IntStream.rangeClosed(0, n)
            .boxed()
            .sorted((i1, i2) -> ThreadLocalRandom.current().nextInt(2 * n   1) - n)
            .map(i -> i == n ? question.getCorrectAnswer() : question.getIncorrectAnswers().get(i))
            .collect(Collectors.toMap(s -> k[0]  , Function.identity()));
}

These methods provide similar results:

for (int i = 0; i < 3; i  ) {
    System.out.println(buildOptions(new Question("good",
                                                  Arrays.asList("invalid", "poor", "misfit", "imprecise"))));
    System.out.println(buildOptionsIndex(new Question("correct",
                                                  Arrays.asList("bad", "incorrect", "wrong", "inaccurate"))));
    System.out.println("----");
}

Output

{1=invalid, 2=misfit, 3=good, 4=poor, 5=imprecise}
{1=inaccurate, 2=wrong, 3=incorrect, 4=bad, 5=correct}
----
{1=good, 2=misfit, 3=invalid, 4=poor, 5=imprecise}
{1=bad, 2=incorrect, 3=wrong, 4=correct, 5=inaccurate}
----
{1=poor, 2=misfit, 3=invalid, 4=good, 5=imprecise}
{1=bad, 2=incorrect, 3=correct, 4=wrong, 5=inaccurate}
----
  • Related