digitig
digitig

Reputation: 2140

F#: Returning a list with element at index changed

In F# I need to get a list from an existing list with the value at a particular index changed from the original. All I can find on google and here relates to changing something based on value rather than index (fair enough, because lists don't really have a concept of "index", but I bet everyone knew what I meant).

This is obviously easy enough to code. At the moment I have:

// Replace element at index with newElement in seq. Does nothing if index is outside seq.
let updateElement index newElement seq =
    let rec updateElementHelper seq count result =
        match seq with
        | [] -> result |> List.rev
        | head::tail ->
            if count = index then 
                updateElementHelper [] (count + 1) (newElement::result)@tail
            else
                updateElementHelper tail (count + 1) (head::result)
    updateElementHelper seq 0 []

which seems to work just fine, but is there a more native way than this?

(F# newbie - or rather, returing after a very long break and never having got all that far the first time around).

Upvotes: 0

Views: 708

Answers (2)

Tomas Petricek
Tomas Petricek

Reputation: 243096

The easiest way to implement this is probably to use the List.mapi function - it calls a function you provide for each element of the list and gives you an index, so you can either return the original element or your new element, depending on the index:

let updateElement index element list = 
  list |> List.mapi (fun i v -> if i = index then element else v)

updateElement 4 40 [ 0 .. 9 ]

As noted by @Jarak, if you need to do this often, then it might be worth thinking whether there is some other more functional approach to your problem where you do not rely on indices - doing something like this would not be very typical thing to do in functional code.

Upvotes: 2

Jarak
Jarak

Reputation: 982

I am assuming that you don't want to allow the list to be mutable. If you did, then you could just index into the list and update the value, e.g. mylist.[index] <- newValue.

I will say right now that any operation on a list that uses any other sort of access than the typical "head + tail -> recurse on tail" style is a strong sign that a list isn't the right data structure for your operation. See e.g. Juliet's answer here. Typically, if you want to be operating on a linear data structure by index, an array is your best bet.

The easiest way I can think of to do this if you still want to do it with a list, would be something like the following:

let newList = oldList.[..index - 1] @ (newValue :: oldList.[index + 1..])

(I might possibly have the indices slightly off)

This will probably have very poor performance, however. I think it would be reasonably fair to say that many F#-ers would call any use of @ or List slicing a code smell. As a very infrequent operation on small lists it might be alright, but if it will be used frequently, or on large lists, then it would be a good time to start thinking if a list is really the right collection data structure for your task.

Upvotes: 2

Related Questions