I have been looking for a more concise way to code this (below). Basically, I need a way to determine if only one of a set of properties does not equal its counterpart on a different type (address
and alternateAddress
are different types). If only ONE of the 5 values is doesn't match then I want a specific error, otherwise I want a more general error.
Note the ==
and !=
are custom infix operators we have for case-insensitive comparisons.
open System
type Errors =
| InvalidStreet
| InvalidCity
| InvalidState
| InvalidPostalCode
| InvalidCountry
| InvalidAddress
type Address =
{
Street: string
City: string
Region: string
PostalCode: string
Country: string
}
type AlternateAddress =
{
Street: string
City: string
Region: string
PostalCode: string
Country: string
}
let inline (==) (s1: string) (s2: string) = s1.Equals(s2, StringComparison.CurrentCultureIgnoreCase)
let inline (!=) s1 s2 = s1 == s2 |> not
let address = {Address.Street = "123 Main St."; City = "Happytown"; Region = "CA"; PostalCode = "90210"; Country = "USA"}
let alternateAddress = Some {AlternateAddress.Street = "123 Main"; City = "Happytown"; Region = "CA"; PostalCode = "90210"; Country = "USA"}
match alternateAddress with
| Some alternateAddress ->
if
address.Street != alternateAddress.Street
&& address.City == alternateAddress.City
&& address.Region == alternateAddress.Region
&& address.PostalCode == alternateAddress.PostalCode
&& address.Country == alternateAddress.Country
then InvalidStreet
elif
address.Street == alternateAddress.Street
&& address.City != alternateAddress.City
&& address.Region == alternateAddress.Region
&& address.PostalCode == alternateAddress.PostalCode
&& address.Country == alternateAddress.Country
then InvalidCity
elif
address.Street == alternateAddress.Street
&& address.City == alternateAddress.City
&& address.Region != alternateAddress.Region
&& address.PostalCode == alternateAddress.PostalCode
&& address.Country == alternateAddress.Country
then InvalidState
elif
address.Street == alternateAddress.Street
&& address.City == alternateAddress.City
&& address.Region == alternateAddress.Region
&& address.PostalCode != alternateAddress.PostalCode
&& address.Country == alternateAddress.Country
then InvalidPostalCode
elif
address.Street == alternateAddress.Street
&& address.City == alternateAddress.City
&& address.Region == alternateAddress.Region
&& address.PostalCode == alternateAddress.PostalCode
&& address.Country != alternateAddress.Country
then InvalidCountry
else InvalidAddress
| _ -> InvalidAddress
One way to do this would be to define a list of corresponding "getters" (functions that take one of your records and return a string) for each of the fields together with associated error. For simplicity, I made alternateAddress
non-optional:
let comparers : ((Address -> _) * (AlternateAddress -> _) * _) list =
[ (fun x -> x.Street), (fun x -> x.Street), InvalidStreet;
(fun x -> x.City), (fun x -> x.City), InvalidStreet
(fun x -> x.Region), (fun x -> x.Region), InvalidStreet
(fun x -> x.PostalCode), (fun x -> x.PostalCode), InvalidStreet
(fun x -> x.Country), (fun x -> x.Country), InvalidStreet ]
Now, you can iterate over comparers
and use the first function to get a field from Address
, second function to get a field from AlternateAddress
and if they do not match, return the third element of the tuple, which is the error to report.
You can use List.choose
to get a list that is empty when all fields match and, otherwise, contains a list of errors when there were mismatching fields:
let errors = comparers |> List.choose (fun (getAddr, getAlt, err) ->
if getAddr address != getAlt alternateAddress then Some err else None)
The errors
list will be empty if there are no issues, contain one error if there was just one error or contain multiple errors:
match errors with
| [] -> printfn "All good!"
| [err] -> printfn "One error: %A" err
| _ -> printfn "Multiple errors!"
It is worth noting that depending on your particular situation, it might perhaps be a good idea to restructure the code to make this operation easier - but it's hard to say without knowing more about your situation.