Home > Mobile >  How to number list elements in Elm?
How to number list elements in Elm?

Time:10-19

Given the following list

  List Records

where Record is

   type Record
   = RecordA A
   | RecordB B

and A and B are type aliases with pos field:

  type alias A =
  { pos : Maybe Int
  , type : Char
  }

how would I create a new list that numbers subsequent A and B records? I want to have pos = 1 in the first A record, pos = 2 in the second A record, pos = 1 in the first B record, etc. (The original list comes without numbers in the pos field.)

This is a solution for a language that allows mutation.

let countA = 0;
let countB = 0;
for (el of my_array) {
    el.pos = (el.type == 'A') ?   countA :   countB;
}

CodePudding user response:

You can convert any iterative algorithm using mutable variables into a recursive one that don't quite easily by just making the mutable variables function arguments:

enumerate : List Record -> Int -> Int -> List Record
enumerate list countA countB =
    case list of
        [] ->
            []

        (RecordA el) :: rest ->
            RecordA { el | pos = Just countA } :: enumerate rest (countA   1) countB

        (RecordB el) :: rest ->
            RecordB { el | pos = Just countB } :: enumerate rest countA (countB   1)

This is not tail recursive however, and will therefore overflow the stack on larger lists. It's also a bit inconvenient to have to specify the initial counts every time we use this. We can solve both by using an inner function and adding an accumulator argument to it:

enumerate : List Record -> List Record
enumerate list =
    let
        aux els acc posA posB =
            case list of
                [] ->
                    acc

                (RecordA el) :: rest ->
                    aux rest (RecordA { el | pos = Just posA } :: acc) (posA   1) posB

                (RecordB el) :: rest ->
                    aux rest (RecordB { el | pos = Just posB } :: acc) posA (posB   1)
    in
    aux list [] 0 0

You seem to have some deeper data modeling issues here though, but it also doesn't look like your actual types, so hopefully you'll figure that out eventually too. This should at least get you a bit closer.

CodePudding user response:

Lazy response: A map expression might, in essence, do what you want. I haven't checked this thoroughly because I was as well too lazy for the boilerplate to make it actually compile in an ellie though:

List.map (\n -> RecordA (A(Just n) 'a')) (List.range 1 5)

If I get you correctly, you have a list containing both A and B types? Then you might want to pattern match on the types in the function that is mapped over the list (the (\n -> RecordA (A(Just n) 'a')) part), maybe construct two lists and zip them together. But as @glennsl already said, the design leaves some questions open.

  • Related