问题
When I run the code below I get an error. I am using Map.TryFind and its not working. In console, I get a red line under familyinc.TryFind(tract)
and the error below.
let data =
seq { for (state,work) in statecsv do
let (family,income) = familyinc.TryFind(state)
let fam =
match fam with
| Some x -> x
| None -> "Not a Record"
let inc =
match inc with
| Some x -> x
| None -> "Not an income"
yield (state,work,inc,fam)
}
The ERROR:
error FS0001: This expression was expected to have type
''a * 'b'
but here has type
'(string * decimal) option'
回答1:
Answer to the edited question: The problem is the same as in the previous one, you are pattern matching on a tuple while you are binding an option. You should do something like this instead:
// Bind the whole option
let result = familyinc.TryFind(state)
// Pattern match on it
match result with
| Some (family , income) -> yield (state,work,family,income)
| None -> yield (state,work,"Not a Record","Not an Income")
Of course you could also do match familyinc.TryFind(tract) with
, there's no need to bind to variable here.
The issue is you are pattern matching on the result of Map.TryFind() as if it would return a tuple but it actually returns an option
as it may fail to find they key you are looking for.
回答2:
In all FP languages understanding option types and pattern matching is essential. In fact both of these features make FP a superior alternative to OO languages. Using option types you can avoid getting null
exceptions, using pattern matching you can deconstruct values. In this case you can filter out non-existing keys, and convert the option
result into normal values:
//create test dictionary
let map1 = [("a",1); ("b",2);("c",3)] |> Map.ofList
//list of keys, "d" doesn't exist in the dictionary/map
let keys = ["a";"b";"d"]
keys
|> List.map (fun x -> map1.[x])
//System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.
keys
|> List.map (fun x -> map1.TryFind(x))
//You get back a string option list, with the last element missing as the key "d" doesn't exist
//val it : int option list = [Some 1; Some 2; None]
//Method A: filter out the none existing items
keys
|> List.map (fun x -> map1.TryFind(x))
|> List.choose id //choose will get rid of the Nones and return the actual value, not the option. id is the identity function.
//Method B: replace the None with some default value, and also get rid of the option
//Let's say you replace each non existing value with 999
keys
|> List.map (fun x -> map1.TryFind(x))
|> List.map (Option.defaultValue 999)
//val it : int list = [1; 2; 999]
//In general if necessary you can always pattern match
let myOption1 = Some "A"
let myOption2 = None
match myOption1 with
| Some x -> x //try matching whatever is in myOption1 and returns the x portion of Some x, in this case "A"
| None -> "No value"
//val it : string = "A"
match myOption2 with
| Some x -> x
| None -> "No value" //since the value of myOption2 is None, Some x won't match, None will match, and return "No value"
//val it : string = "No value"
来源:https://stackoverflow.com/questions/50884960/how-to-use-map-tryfind