Remove one non-unique value from a sequence in F #

I have a sequence of integers representing bones in F #.

In the game in question, the player has a pool of dice and can select one of them (controlled by certain rules) and save the rest.

If, for example, a player rolls 6, 6, and 4 and decides to play one six, is there an easy way to return a sequence with only one deleted?

Seq.filter (fun x -> x != 6) dice 

removes all poles, not just one.

+3
source share
5 answers

the code below will work for a list (so not any seq, but it looks like the sequence you are using might be List)

 let rec removeOne value list = match list with | head::tail when head = value -> tail | head::tail -> head::(removeOne value tail) | _ -> [] //you might wanna fail here since it didn't find value in //the list 

EDIT: code updated based on correct comment below. Thanks P

EDIT: After reading another answer, I thought the warning would be ok. Do not use the code above for infinite sequences, but since I think your players do not have infinite blocks that should not be a problem, but for completeness, an implementation is implemented here that will work a (almost) finite sequence

  let rec removeOne value seq acc = match seq.Any() with | true when s.First() = value -> seq.Skip(1) | true -> seq.First()::(removeOne value seq.Skip(1)) | _ -> List.rev acc //you might wanna fail here since it didn't find value in //the list 

However, I recommend using the first solution, which Im sure will work better than the last, even if you need to first rotate the sequence into a list (at least for small sequences or large sequences with the desired value at the end)

+1
source

Nontrivial sequence operations are painful to work with because they do not support pattern matching. I think the simplest solution is as follows:

 let filterFirst fs = seq { let filtered = ref false for a in s do if filtered.Value = false && fa then filtered := true else yield a } 

As long as the modified implementation is hidden from the client, this is still a functional style;)

+4
source

If you are going to store data, I would use ResizeArray instead of Sequence. It has many functions, such as the function you requested. It is simply called Delete. Note : ResizeArray is an abbreviation for the CLI type List .

 let test = seq [1; 2; 6; 6; 1; 0] let a = new ResizeArray<int>(test) a.Remove 6 |> ignore Seq.toList a |> printf "%A" // output > [1; 2; 6; 1; 0] 

Other data type parameters may be arrays.

 let removeOneFromArray va = let i = Array.findIndex ((=)v) a Array.append a.[..(i-1)] a.[(i+1)..] 

or list

 let removeOneFromList vl = let rec remove acc = function | x::xs when x = v -> List.rev acc @ xs | x::xs -> remove (x::acc) xs | [] -> acc remove [] l 
+2
source

I don’t think there is any function that would allow you to directly present the idea that you want to remove only the first element that matches the specified criteria from the list (for example, something like Seq.removeOne ).

You can implement the function in a relatively easy to read way using Seq.fold (if the sequence of numbers is finite):

 let removeOne fl = Seq.fold (fun (removed, res) v -> if removed then true, v::res elif fv then true, res else false, v::res) (false, []) l |> snd |> List.rev > removeOne (fun x -> x = 6) [ 1; 2; 6; 6; 1 ]; val it : int list = [1; 2; 6; 1] 

The fold function saves some state β€” in this case, the type bool * list<'a> . The Boolean flag means that we have already deleted an element, and the list is used to accumulate the result (which should be canceled at the end of processing).

If you need to do this for an (possibly) infinite seq<int> , you will need to use GetEnumerator directly and implement the code as an expression of a recursive sequence. This is a little uglier and it will look like this:

 let removeOne f (s:seq<_>) = // Get enumerator of the input sequence let en = s.GetEnumerator() let rec loop() = seq { // Move to the next element if en.MoveNext() then // Is this the element to skip? if f en.Current then // Yes - return all remaining elements without filtering while en.MoveNext() do yield en.Current else // No - return this element and continue looping yield en.Current yield! loop() } loop() 
+1
source

You can try this:

 let rec removeFirstOccurrence item screened items = items |> function | h::tail -> if h = item then screened @ tail else tail |> removeFirstOccurrence item (screened @ [h]) | _ -> [] 

Using:

 let updated = products |> removeFirstOccurrence product [] 
0
source

All Articles