This is a set of libraries that contain Scala collections and collection-oriented utilities that I've found useful. It was previously a sub-project of tmccarthy/tmmUtils, but it has reached a level of maturity that justifies splitting it out.
The libraries have been published to Maven central. They can be used by adding the following to your build.sbt
val tmmCollectionsVersion = "0.0.1"
libraryDependencies += "au.id.tmm.tmm-scala-collections" %% "tmm-scala-collections-core" % tmmCollectionsVersion,
libraryDependencies += "au.id.tmm.tmm-scala-collections" %% "tmm-scala-collections-circe" % tmmCollectionsVersion,
libraryDependencies += "au.id.tmm.tmm-scala-collections" %% "tmm-scala-collections-cats" % tmmCollectionsVersion,
libraryDependencies += "au.id.tmm.tmm-scala-collections" %% "tmm-scala-collections-scalacheck" % tmmCollectionsVersion % Test,
The following collections are provided by the tmm-scala-collections-core
artefact:
DupelessSeq
: A (poorly-named) collection which can be thought of as a sequence without duplicates, and with a constant-timecontains
check. It differs fromListSet
(and other sets that retain insertion order) in that element order is considered for equality.NonEmptyDupelessSeq
: ADupelessSeq
that is guaranteed to be non-empty.NonEmptySet
: A set that is guaranteed to be non-empty. Unlike theNonEmptySet
provided by Cats, this collection relies on universal equality in the same way as the Scala set. This makes it easier to use, as elements do not require anOrder
instance, but comes at the cost of strict-lawfulness.NonEmptyMap
: A map that is guaranteed to be non-empty. Again, likeNonEmptySet
, this differs from the CatsNonEmptyMap
in that it relies on universal equality rather than requiring anOrder
instance for keys.NonEmptyArraySeq
: An immutableArraySeq
that is guaranteed to be non-empty.
The other modules in this project provide integration between the above collections and some popular FP libraries:
tmm-scala-collections-scalacheck
provides integration with ScalaCheck to support property-based testing.tmm-scala-collections-circe
provides codecs for use with the Circe json library.tmm-scala-collections-cats
provides extensive lawful and unlawful instances for the collections above.
import au.id.tmm.collections.syntax._
The tmm-scala-collections-core
project provides a few useful syntax extensions via the above import. Some highlights
are outlined below:
The groupBy
method provided by the IterableOps
class can be improved by reflecting the non-empty nature of the groups in the type
signature. The safeGroupBy
syntax provides this:
import au.id.tmm.collections.NonEmptySet
import au.id.tmm.collections.syntax._
import au.id.tmm.collections.cats.instances.all._
import cats.data.NonEmptyList
val list = List("apple", "apricot", "banana")
val set = Set("apple", "apricot", "banana")
// ❌ BAD, return type doesn't indicate that groups cannot be empty
val _: Map[Char, List[String]] = list.groupBy(_.head)
val _: Map[Char, Set[String]] = set.groupBy(_.head)
// ✅ GOOD, return type indicates that groups are non-empty
val _: Map[Char, NonEmptyList[String]] = list.safeGroupBy(_.head)
val _: Map[Char, NonEmptySet[String]] = set.safeGroupBy(_.head)
The orError
syntaxes allow for easily extracting elements from Iterable
collections based on the number of elements.
I have found this valuable when processing and cleaning data from external sources:
onlyElementOrException
returns aRight
containing the element if the collection has exactly one element,Left
otherwise.emptyOrException
returnsRight(())
if the collection is empty,Left
otherwiseatMostOne
returnsRight(None)
for empty collections,Right(Some)
for those with one element, andLeft
for two or more elements.