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}
----