Search code examples
ceylon

Updating an immutable sequence


I have a sequence in Ceylon and I want to make a new sequence with one of the elements replaced with something else according an index:

[String*] strings = ["zero", "one", "two"];
value index = 1;
value newElement= "uno";
[String*]? newStrings = ???; // should be ["zero", "uno", "two"]

In Scala, this is called update.


Solution

  • There are several ways to solve this problem, and I do like the solution by Quintesse above, using Array. That is, perhaps, what I would do in practice. However, there is one possibly-important downside of the Array solution: it allocates memory twice.

    So let me just suggest a couple of different options, for the sake of completeness:

    Using stream operations

    This works, but is a bit verbose:

    [String*] strings = ["zero", "one", "two"];
    value index = 1;
    value newElement= "uno";
    [String*] newStrings = 
        strings.take(index)
               .chain(strings.skip(index+1).follow(newElement))
               .sequence();
    

    Note that here we are using the lazy stream operations take(), skip(), chain() and follow() to create a lazy stream of elements, and then the sequence() operation to take a copy into a new sequence. It allocates just once, in the call to sequence().

    Using patch()

    This also works:

    [String*] strings = ["zero", "one", "two"];
    value index = 1;
    value newElement= "uno";
    [String*] newStrings = 
            strings.patch([newElement], index, 1)
                   .sequence();
    

    Note that if it's good enough to get back an immutable List, you could drop the call to sequence(), resulting in:

    [String*] strings = ["zero", "one", "two"];
    value index = 1;
    value newElement= "uno";
    [String*] newStrings = 
            strings.patch([newElement], index, 1);
    

    And in this case there's no allocation at all (except for one trivial instance of List.Patch).

    See the docs for List.patch().