I have a line of code like this:
list |> List.mapi (fun i x -> y, i)
(Assume that y is already defined type)
But I want to return elements with some condition (e.g filter it)
I am not able to write like this:
list |> List.mapi (fun i x -> if 'condition' then y, i)
because it needs else condition as well and I don't have 'else' case. I also didn't manage to use filter at the same time, because I need to return correct indexes as well, and if I filter the list, indexes will be changed. Any ideas?
EDIT By now, I implemented like this:
list |> List.mapi (fun i a -> if (a = None) then O, i else X, i) |> List.filter (fun (a,i) -> a = O)
I'm giving useless X,i for else case, just to be able to write condition after that and remove X ones. It's working, that's the result I want. But I'm sure there is a better solution.
If you want to filter, but have indexes applied in strictly monotonically increasing order after filtering, then filter first, and then add the index values:
list |> List.filter condition |> List.mapi (fun i x -> x, i)
Here's an example where b
is filtered away from an alphabetical list of characters:
[('a', 0); ('c', 1); ('d', 2); ('e', 3); ('f', 4); ('g', 5); ('h', 6);
('i', 7); ('j', 8); ('k', 9); ('l', 10); ('m', 11); ('n', 12); ('o', 13)]
Let me add yet another answer: From your question and comments I understand that you want to filter a list by a condition depending on the values while retaining the original indexes. I'm not sure whether the result should then consist of a list of fixed values and original index or you want to map. The following allows both:
let indexedFilterMap p f =
List.indexed
>> List.filter (snd >> p)
>> List.map (fun (i, x) -> f x, i)
If you need the index for the mapping (as the question title includes mapi
):
let indexedFilterMapi p f =
List.indexed
>> List.filter (snd >> p)
>> List.map f
Or if you need the index for the filter:
let indexedFilteriMap p f =
List.indexed
>> List.filter p
>> List.map (fun (i, x) -> f x, i)
The combination should be straightforward.
These can then be used:
let list = ['a'; 'b'; 'c']
let condition = (<>) 'b'
let y = "fixed value"
indexedFilterMap condition (fun _ -> y) list // [("fixed value", 0); ("fixed value", 2)]
let m (i, _) = sprintf "fixed value %i" i
indexedFilterMapi condition m list // ["fixed value 0"; "fixed value 2"]
let c (i, _) = i <> 1
indexedFilteriMap c (fun _ -> y) list // [("fixed value", 0); ("fixed value", 2)]
First of all, be aware that looking up by i
from a list is an O(n) operation so if that's what you're doing, there could be a more efficient alternative available by expressing the problem differently.
For the problem as described, you could do something like this:
list
|> List.mapi (fun i x -> x, i)
|> List.choose (fun (x,i) -> if 'condition on x' then Some (y,i) else None)
Returns a list of tuples of y and an element index that satisfied the condition.
Example:
Consider I start with ['a','b','c','d','e']
, the first mapi
maps the list to [('a',0),('b',1),('c',2),('d',3),('e',4)]
then I apply choose
with (for example) a condition that select vowels and returns some value y
. I end up with [(y,4)]
.
Edit: In response to your update, here is an example of using this in precisely the way you want.
list
|> List.mapi (fun i x -> x, i)
|> List.choose (fun (x,i) ->
match x with
|O -> Some (O, i)
|X -> None)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With