Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

F# filter out only first occurrence from list

Tags:

filter

f#

fold

I have a list and I want to remove an element matching some criteria but remove only one element.

let items = [1;2;3]

let predicate x =
    x >= 2

let result = items |> List.fold ...
// result = [1;3]

How to achieve method returning list with [1;3]?

like image 363
Krzysztof Morcinek Avatar asked Sep 04 '17 14:09

Krzysztof Morcinek


2 Answers

You can use a generic recursive function

let rec removeFirst predicate = function
    | [] -> []
    | h :: t when predicate h -> t
    | h :: t -> h :: removeFirst predicate t

or a tail recursive one (if you fear a stack overflow)

let removeFirst predicate list =
    let rec loop acc = function
        | [] -> List.rev acc
        | h :: t when predicate h -> (List.rev acc) @ t
        | h :: t -> loop (h :: acc) t
    loop [] list
like image 175
gileCAD Avatar answered Oct 02 '22 14:10

gileCAD


let result =
    items
    |>List.scan (fun (removed, _) item ->
        if removed then true, Some(item) //If already removed, just propagate
        elif predicate item then true, None //If not removed but predicate matches, don't propagate
        else false, Some(item)) //If not removed and predicate doesn't match, propagate
        (false, None)
    |>List.choose snd

The state is a tuple. The first element is a Boolean flag indicating whether we already have removed some item from the list. The second element is an option: Some when we want to emit the item, None otherwise.

The last line takes the second elements from the states and for each of them emits the wrapped value (in case of Some) or does nothing (in case of None).

like image 25
tearvisus Avatar answered Oct 02 '22 15:10

tearvisus