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-timecontainscheck. It differs fromListSet(and other sets that retain insertion order) in that element order is considered for equality.NonEmptyDupelessSeq: ADupelessSeqthat is guaranteed to be non-empty.NonEmptySet: A set that is guaranteed to be non-empty. Unlike theNonEmptySetprovided 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 anOrderinstance, but comes at the cost of strict-lawfulness.NonEmptyMap: A map that is guaranteed to be non-empty. Again, likeNonEmptySet, this differs from the CatsNonEmptyMapin that it relies on universal equality rather than requiring anOrderinstance for keys.NonEmptyArraySeq: An immutableArraySeqthat 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-scalacheckprovides integration with ScalaCheck to support property-based testing.tmm-scala-collections-circeprovides codecs for use with the Circe json library.tmm-scala-collections-catsprovides 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:
onlyElementOrExceptionreturns aRightcontaining the element if the collection has exactly one element,Leftotherwise.emptyOrExceptionreturnsRight(())if the collection is empty,LeftotherwiseatMostOnereturnsRight(None)for empty collections,Right(Some)for those with one element, andLeftfor two or more elements.