Konata
Konata

Reputation: 81

Remove a single element from an array

I've been at this for a few hours now, looking though every website and piece of documentation I could. I can't figure out how to remove one, and only one element (In this case, a string) from an array, keeping any duplicates in tact.

I did find a way, but, it's absolutely atrocious:

let remItem gs item = 
    if (chkItem gs item) then
        let mutable fr = [| |] //temporary array
        let mutable don = false //check if we found the element
        for i in gs.inventory do
            if not (i = item) && don then
                fr <- (Array.append fr [|i|])
            //add to the temp array until we find our item
            elif i = item && don = false then don <- true
            //we found it, skip just once so it doesn't get added
            elif don then fr <- (Array.append fr [|i|])
            //now just add everything else to the temp array
        { gs with inventory = fr }
    else gs

I wrote this and I barely know how it works. Please tell me there's a better way to do this. I know the mutable variables aren't needed, but I've written a dozen equally horrendous-looking pure functions and concluded this is the best that I could do. I've tried a lot of the Array.* recursive functions already, I can't seem to make any of those comply with what I want either. I just want to know if it's even possible to do this neatly and purely in F#.

Upvotes: 2

Views: 1750

Answers (3)

Kevin
Kevin

Reputation: 2291

Fold should do the trick:

let remove x a =
  Array.fold  
    (fun (s,found) t -> 
      if found || t <> x then Array.append s [|t|],found
      else s,true) ([||],false) a |> fst

Example usage:

remove 2 [|1; 2; 3; 4; 2; 5|]
val it : int [] = [|1; 3; 4; 2; 5|]

Upvotes: 0

Random Dev
Random Dev

Reputation: 52280

I think the easiest way to do this is to first look for the index (it's an array after all) and then just cut this out - this is (I think) a good compromise between performance and pureness - it's a pure operation but you don't get to much copy-operations:

let remove x (xs : 'a array) =
    match Array.tryFindIndex ((=) x) xs with
    | Some 0 -> xs.[1..]
    | Some i -> Array.append xs.[..i-1] xs.[i+1..]
    | None   -> xs

please note that you have to take care of it beeing the first index because xs.[..(-1)] will throw an exception (while the other edge-case is ok):

> remove 0 [|1..10|];;
val it : int [] = [|1; 2; 3; 4; 5; 6; 7; 8; 9; 10|]
> remove 1 [|1..10|];;
val it : int [] = [|2; 3; 4; 5; 6; 7; 8; 9; 10|]
> remove 3 [|1..10|];;
val it : int [] = [|1; 2; 4; 5; 6; 7; 8; 9; 10|]
> remove 9 [|1..10|];;
val it : int [] = [|1; 2; 3; 4; 5; 6; 7; 8; 10|]
> remove 10 [|1..10|];;
val it : int [] = [|1; 2; 3; 4; 5; 6; 7; 8; 9|]
> remove 11 [|1..10|];;
val it : int [] = [|1; 2; 3; 4; 5; 6; 7; 8; 9; 10|]

if you need even more performance you could create an empty array and use a more imperative style to copy the parts:

let remove x (xs : 'a array) =
    match Array.tryFindIndex ((=) x) xs with
    | Some i -> 
        let res = Array.zeroCreate (xs.Length-1)
        if i >= 1 then
            System.Array.Copy(xs,0,res,0,i)
        if i+1 < xs.Length then
            System.Array.Copy(xs,i+1,res,i,xs.Length-i-1)
        res
    | None -> xs

Upvotes: 5

Joel Gregory
Joel Gregory

Reputation: 469

Remove the first occurrence of a item from a list (taken from http://www.fssnip.net/1T):

let rec remove_first pred lst =
    match lst with
    | h::t when pred h -> t
    | h::t -> h::remove_first pred t
    | _ -> []

Usage:

let somelist = [('a',2);('f',7);('a',4);('h',10)] 
let removed = somelist |> remove_first (fun (x,y) -> x='a')

// Result is:
// [('f',7);('a',4);('h',10)]

Upvotes: 0

Related Questions