3
votes

How should one pipeline an index into an indexer property of eg.: a Map?

let aMap = [
    (1,'a')
    (2,'b')
    ] |> Map.ofList

let someChar =
    1
    |> aMap.Item

In the example above, I get the error that "An indexer property must be given at least one argument"

The following works, but in real code gets kind of ugly and strange looking. Is there a better way? What is the language reason for not accepting the pipelined item for the indexer property? is it because it is a getter property instead of a simple method?

let someChar =
    1
    |> fun index -> aMap.[index]

Edit: below is a better representation of the actual usage scenario with the solution I went with inside a transformation pipeline eg.:

let data =
    someList
    |> List.map (fun i ->
        getIndexFromData i
        |> Map.find <| aMap
        |> someOtherFunctionOnTheData
        )
    |> (*...*)

2

2 Answers

2
votes

As a rule of thumb, you shouldn't pipeline into an indexer property. Use indexers explicitly.

For F# collections modules, there usually exists a lookup function that can be used instead of index access (Array.get, Map.find, Set.contains).

2
votes

The Item indexer actually has a an associated getter method get_Item which F# compiler wisely hides from the auto-completion so that people do not use it, but it does allow you to use it:

let aMap = 
  [ (1,'a')
    (2,'b') ] |> Map.ofList

let someChar =
  1 |> aMap.get_Item

That said, I cannot imagine a situation where I would want to do this. Pipelines make sense in cases where you're chaining multiple operations on the same data structure (list transformations, etc.), but using pipeline as a general mechanism for invoking an operation is just introducing a more cumbersome syntax for doing a simple thing.

The point of indexers is to make it clear that you are indexing into some collection. Using that directly makes your code much more readable:

let someChar = aMap.[1]