I have a set of records:
type Person =
{
Name : string
Age : int
}
let oldPeople =
set [ { Name = "The Doctor"; Age = 1500 };
{ Name = "Yoda"; Age = 900 } ]
Unlike the hardcoded example above, the set of data actually comes from a data source (over which I have very little control). Now I need to subtract a set of data from another data source. In general, the data in this second source matches, but occasionally there is a difference in captialization:
let peopleWhoAreConfusedAboutTheirAge =
set [ { Name = "THE DOCTOR"; Age = 1500 } ]
When I attempt to subtract the second set from the first, it fails because the string comparison is case sensitive:
let peopleWhoKnowHowOldTheyAre =
oldPeople - peopleWhoAreConfusedAboutTheirAge
val peopleWhoKnowHowOldTheyAre : Set<Person> =
set [{Name = "The Doctor";
Age = 1500;}; {Name = "Yoda";
Age = 900;}]
Is there a way to perform a case-insensitive comparison for the Name
field of the People
record?
This is what I've implemented so far, though there may be a better way to do it.
My solution was to override the Equals
function on the People
record so as to perform a case-insensitive comparison. Set subtraction uses the Equals
function to determine if two records match one another. By overriding Equals
, I was forced (via warning and error) to override GetHashCode
and implement IComparable
(as well as set the CustomEquality
and CustomComparison
attributes):
[<CustomEquality; CustomComparison>]
type Person =
{
Name : string
Age : int
}
member private this._internalId =
this.Name.ToLower() + this.Age.ToString()
interface System.IComparable with
member this.CompareTo obj =
let other : Person = downcast obj
this._internalId.CompareTo( other._internalId )
override this.Equals( other ) =
match other with
| :? Person as other ->
System.String.Compare( this._internalId, other._internalId ) = 0
| _ -> false
override this.GetHashCode() =
this._internalId.GetHashCode()
This, however, seems to do the trick:
let oldPeople =
set [ { Name = "The Doctor"; Age = 1500 };
{ Name = "Yoda"; Age = 900 } ]
let peopleWhoAreConfusedAboutTheirAge =
set [ { Name = "THE DOCTOR"; Age = 1500 } ]
let peopleWhoKnowHowOldTheyAre =
oldPeople - peopleWhoAreConfusedAboutTheirAge
val peopleWhoKnowHowOldTheyAre : Set<Person> = set [{Name = "Yoda";
Age = 900;}]
If you know a better solution (involving less code), please post it rather than comment on this answer. I will happily accept a less verbose, awkward solution.
Here's another approach:
type Name(value) =
member val Value = value
override this.Equals(that) =
match that with
| :? Name as name -> StringComparer.CurrentCultureIgnoreCase.Equals(this.Value, name.Value)
| _ -> false
override this.GetHashCode() =
StringComparer.CurrentCultureIgnoreCase.GetHashCode(this.Value)
type Person =
{
Name: Name
Age: int
}
{Name=Name("John"); Age=21} = {Name=Name("john"); Age=21} //true