ceylon

Ceylon equivalent of Collections.shuffle()


Is there a way to "shuffle" up an Iterable (or Sequence), such that elements are subsequently ordered randomly, similar to Java's Collections.shuffle()? I've looked in the API docs for Iterable, Collection, and Sequence, but haven't found anything relevant. (Side note: ceylon.language::shuffle is confusingly named)

I could implement a shuffle myself I suppose, but I'm busy trying to be lazy :-)


Solution

  • I also went looking for this and couldn't find it. Here's an implementation:

    import ceylon.collection {ArrayList}
    import ceylon.math.float {random}
    
    "Don't interleave multiple iterators!"
    Iterable<Element, Absent> shuffle<Element, Absent>(Iterable<Element, Absent> elements)
            given Absent satisfies Null => object satisfies Iterable<Element, Absent> {
        value list = ArrayList{elements = elements;};
        iterator() => object satisfies Iterator<Element> {
            variable value index = list.size;
            shared actual Element|Finished next() {
                value randomIndex = (random() * index--).integer;
                if (exists element = list[index]) {
                    assert (exists randomElement = list[randomIndex]);
                    list.set(index, randomElement);
                    list.set(randomIndex, element);
                    return randomElement;
                }
                return finished;
            }
        };
    };