@@ -27,6 +27,7 @@ module Rel8.Tabulate
2727 , difference
2828 , aggregateTabulation
2929 , orderTabulation
30+ , singularize
3031 , optionalTabulation
3132 , manyTabulation
3233 , someTabulation
@@ -55,7 +56,7 @@ import Rel8.Query.Maybe ( optional )
5556import Rel8.Query.Order ( orderBy )
5657import Rel8.Query.These ( alignBy )
5758import Rel8.Table ( Table )
58−import Rel8.Table.Aggregate ( groupBy, listAgg, nonEmptyAgg )
59+import Rel8.Table.Aggregate ( groupBy, headAgg, listAgg, nonEmptyAgg )
5960import Rel8.Table.Alternative
6061 ( AltTable, (<|>:)
6162 , AlternativeTable, emptyTable
@@ -368,6 +369,14 @@ orderTabulation ordering (Tabulation as) =
368369 Tabulation $ orderBy (snd >$< ordering) . as
369370
370371
372+-- | Turns the given 'Tabulation' from a \"multimap\" into a \"map\". If there
373+-- is more than one value at a particular key, only the first one is kept.
374+-- \"First\" is in general undefined, but 'orderTabulation' can be used to
375+-- make it deterministic.
376+singularize :: (EqTable k, Table Expr a) => Tabulation k a -> Tabulation k a
377+singularize = aggregateTabulation . fmap headAgg
378+
379+
371380optionalTabulation :: EqTable k
372381 => Tabulation k a -> Tabulation k (MaybeTable a)
373382optionalTabulation as = Tabulation $ \case