I want to make a collection, like a set or dict, where the keys are embedded in the items. For example, suppose there is a Person
type with a social-security field. Notice that when I add a person to the collection I need to specify the SSN even though it is part of the Person record. This isn’t that big a deal but it is possible to accidentally specify the wrong SSN (one that doesn’t match what is in the record). It would be cleaner if I could just do something like Set.add(mike)
and it would know to use the SSN field in the record as the “key” and I could look up people only using that key, like Set.get("561"->SocialSecurity.makeExn)
. Is this possible with the built-in Belt collections? I see a collection called SetDict
and thought maybe it would work for this scenario but I can’t figure it out. I also thought maybe I could use the normal Set
but it seems like if I put a whole Person in then when I query I need to query for a whole Person, not just the SSN. Part of the problem is I’m a little confused by the whole MakeComparable
thing; I thought I could do a MakeComparable
on Person and only compare the ssn, but I can’t figure out how to use one of these created modules to make a Set or Dict that takes a whole Person when inserting data, and allows querying just with the SSN.
open Belt
module SocialSecurity = {
@unboxed
type t = Ssn(string)
let make = s => {
let s = s->Js.String2.trim
if s->Js.String2.length < 10 {
None
} else {
Some(Ssn(s))
}
}
let makeExn = s => s->make->Option.getExn
let cmp = (a: t, b: t) => Pervasives.compare(a, b)
}
module SocialSecurityCmp = Id.MakeComparable(SocialSecurity)
module Person = {
type t = {
ssn: SocialSecurity.t,
name: string,
age: int,
}
let ssn = p => p.ssn
}
let bob: Person.t = {ssn: "561"->SocialSecurity.makeExn, name: "Bob", age: 32}
let mike: Person.t = {ssn: "976"->SocialSecurity.makeExn, name: "Mike", age: 12}
let addressBook =
Map.make(~id=module(SocialSecurityCmp))
->Map.set(bob->Person.ssn, bob) // specify SSN even though part of record
->Map.set(mike->Person.ssn, mike) // specify SSN even though part of record
let findSomeone = addressBook->Map.get(SocialSecurity.makeExn("561"))