├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── RELEASE.md ├── build.sbt ├── library └── src │ └── main │ └── scala │ └── de │ └── ummels │ └── prioritymap │ ├── PriorityMap.scala │ ├── PriorityMapBuilder.scala │ ├── PriorityMapFactory.scala │ ├── PriorityMapLike.scala │ └── StandardPriorityMap.scala ├── project ├── build.properties └── plugins.sbt ├── root-doc.txt ├── scalastyle-config.xml ├── tests ├── js │ └── src │ │ └── test │ │ └── scala │ │ └── de │ │ └── ummels │ │ └── prioritymap │ │ └── PriorityMapSpec.scala ├── jvm │ └── src │ │ └── test │ │ └── scala │ │ └── de │ │ └── ummels │ │ └── prioritymap │ │ └── PriorityMapSpec.scala └── shared │ └── src │ └── test │ └── scala │ └── de │ └── ummels │ └── prioritymap │ ├── ParProperties.scala │ ├── Properties.scala │ └── PropertySpec.scala └── version.sbt /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: scala 2 | sudo: false 3 | matrix: 4 | include: 5 | - scala: 2.10.6 6 | jdk: openjdk6 7 | - scala: 2.11.8 8 | jdk: openjdk6 9 | - scala: 2.12.0 10 | jdk: oraclejdk8 11 | script: sbt ++$TRAVIS_SCALA_VERSION validate 12 | after_success: 13 | - | 14 | [[ $TRAVIS_PULL_REQUEST = false ]] && 15 | [[ $TRAVIS_BRANCH = master ]] && 16 | sbt ++$TRAVIS_SCALA_VERSION publishSnapshot 17 | - | 18 | [[ $TRAVIS_PULL_REQUEST = false ]] && 19 | [[ $TRAVIS_BRANCH = master ]] && 20 | [[ $TRAVIS_SCALA_VERSION = 2.12.* ]] && 21 | bash <(curl -s https://codecov.io/bash) 22 | - | 23 | [[ $TRAVIS_PULL_REQUEST = false ]] && 24 | [[ $TRAVIS_BRANCH = master ]] && 25 | [[ $TRAVIS_SCALA_VERSION = 2.12.* ]] && 26 | git config --global user.email "travis@travis-ci.org" && 27 | git config --global user.name "Travis CI" && 28 | git config --global push.default simple && 29 | sbt makeSite ghpagesPushSite > /dev/null 30 | env: 31 | global: 32 | - secure: eg/nDoGryerr4xT37aojC5M7YsOnWg0nct/8BJZcF4hd2aJh1kqqYEkwOKMHdVnGGdKy0dHK+mwSrefORcJBAzbV3yluQUgDDmFUVHqjsWhSzu12rBtMhmkaqNRvcznd/hHy509k6+RiQ7iFipE9idpEy3axUTTaWsMJPMLdzFg= 33 | - secure: qB5SyBNN9sXGBNdR2k5eTAykrEVEn/2CCi0Cx2Banli1S/+eYuytqTsxF2umhCe8pd+xn+IlfEresivSTQeXlaxGefU0Pd9Wq7a//Ha8qcHTQZ2YbISeAaNjAvy6WNf25rydLAYzW86CRTQJN4lswvAqkHvYyVuKPcyoxmwpDNw= 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, Michael Ummels 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any 4 | purpose with or without fee is hereby granted, provided that the above 5 | copyright notice and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Immutable priority maps for Scala 2 | 3 | [![Build Status](https://img.shields.io/travis/ummels/scala-prioritymap/master.svg)](https://travis-ci.org/ummels/scala-prioritymap) 4 | [![Coverage](https://img.shields.io/codecov/c/github/ummels/scala-prioritymap/master.svg)](https://codecov.io/github/ummels/scala-prioritymap?branch=master) 5 | [![Maven Central](https://img.shields.io/maven-central/v/de.ummels/scala-prioritymap_2.12.svg)](https://search.maven.org/#search|ga|1|scala-prioritymap) 6 | [![Scala.js](https://www.scala-js.org/assets/badges/scalajs-0.6.13.svg)](https://www.scala-js.org) 7 | 8 | Priority maps are similar to sorted maps, but while for sorted maps `iterator` returns an iterator that 9 | produces entries sorted by their keys, calling `iterator` on a priority map returns an iterator that 10 | produces entries sorted by their *values*. Priority maps also offer several `range` methods, which 11 | return a submap with values inside a given range. 12 | 13 | Since calling `head` on a priority map returns a key-value pair with minimal value, priority 14 | maps can also be thought of as a more versatile variant of *priority queues*. 15 | 16 | This implementation of priority maps has been inspired by 17 | [Mark Engelberg's implentation for Clojure](https://github.com/clojure/data.priority-map). 18 | 19 | ## Setup 20 | 21 | The latest version is 1.0.0 and supports Scala 2.10–2.12 on the JVM as well as on 22 | [Scala.js](http://www.scala-js.org). 23 | 24 | Releases are available from [Maven Central](https://search.maven.org/#search|ga|1|scala-prioritymap). 25 | If you use [sbt](http://www.scala-sbt.org/), simply add the following dependency to your build file: 26 | 27 | ```scala 28 | libraryDependencies += "de.ummels" %%% "scala-prioritymap" % "1.0.0" 29 | ``` 30 | 31 | See the [release notes](RELEASE.md) if you upgrade from an earlier release. 32 | 33 | ## Usage 34 | 35 | The easiest way to instantiate a new priority map is to use the `apply` method in the 36 | `PriorityMap` companion object. 37 | 38 | ```scala 39 | scala> import de.ummels.prioritymap.PriorityMap 40 | import de.ummels.prioritymap.PriorityMap 41 | 42 | scala> val m = PriorityMap('a' -> 1, 'b' -> 2, 'c' -> 0) 43 | m: de.ummels.prioritymap.PriorityMap[Char,Int] = PriorityMap(c -> 0, a -> 1, b -> 2) 44 | ``` 45 | 46 | Since priority maps are immutable, updating a key/value pair returns a new map and does 47 | not modify the old map. 48 | 49 | ```scala 50 | scala> m + ('b' -> -1) 51 | res0: de.ummels.prioritymap.PriorityMap[Char,Int] = PriorityMap(b -> -1, c -> 0, a -> 1) 52 | 53 | scala> m 54 | res1: de.ummels.prioritymap.PriorityMap[Char,Int] = PriorityMap(c -> 0, a -> 1, b -> 2) 55 | ``` 56 | 57 | In addition to the methods available for maps, priority maps offer methods for obtaining 58 | a submap whose values lie inside a given range. 59 | 60 | ```scala 61 | scala> m.from(1) 62 | res2: de.ummels.prioritymap.PriorityMap[Char,Int] = PriorityMap(a -> 1, b -> 2) 63 | 64 | scala> m.until(2) 65 | res3: de.ummels.prioritymap.PriorityMap[Char,Int] = PriorityMap(c -> 0, a -> 1) 66 | 67 | scala> m.range(1, 2) 68 | res4: de.ummels.prioritymap.PriorityMap[Char,Int] = PriorityMap(a -> 1) 69 | ``` 70 | 71 | The full API docs are available [here](http://ummels.github.io/scala-prioritymap/api/). 72 | -------------------------------------------------------------------------------- /RELEASE.md: -------------------------------------------------------------------------------- 1 | # Release Notes 2 | 3 | #### Version 1.0.0 (15 Nov 2016) 4 | 5 | - Add support for Scala 2.12 6 | 7 | #### Version 0.5.0 (18 Oct 2015) 8 | 9 | - Add Scala.js support. 10 | - Rename `DefaultPriorityMap` to `StandardPriorityMap`. 11 | 12 | #### Version 0.3.0 (24 Jan 2015) 13 | 14 | - Fixed behavior of default implementation for custom orderings. 15 | - Fixed preservation of default function after calling `withDefault` or `withDefaultValue`. 16 | - Added builder and factory classes. 17 | - Removed `fromMap` method from companion objects. 18 | - Mader inner class `PriorityMap.WithDefault` private. 19 | 20 | #### Version 0.2.0 (06 Jan 2015) 21 | 22 | - Renamed abstract `range` method to `rangeImpl`. 23 | - Adapted static return types in trait `PriorityMapLike`. 24 | - Made `newBuilder` protected in both `PriorityMap` and `DefaultPriorityMap`. 25 | - Changed static return type of `seq` to `PriorityMap[A, B]`. 26 | - Added methods `firstKey`, `lastKey`, and `merged`. 27 | - Added variants of methods `withDefault` and `withDefaultValue` that return priority maps. 28 | - Optimized performance. 29 | 30 | #### Version 0.1.0 (09 Dec 2014) 31 | 32 | - Initial release. 33 | -------------------------------------------------------------------------------- /build.sbt: -------------------------------------------------------------------------------- 1 | val publishSnapshot = taskKey[Unit]("Publishes snapshot artifacts to a repository.") 2 | 3 | lazy val buildSettings = Seq( 4 | organization := "de.ummels", 5 | description := "Immutable priority maps for Scala", 6 | scalaVersion := "2.12.1", 7 | crossScalaVersions := Seq("2.10.6", "2.11.8", "2.12.1"), 8 | scalacOptions ++= Seq("-Xlint", "-unchecked", "-deprecation", "-feature") 9 | ) 10 | 11 | lazy val docSettings = Seq( 12 | scalacOptions in(Compile, doc) ++= 13 | Seq("-doc-root-content", baseDirectory.value + "/root-doc.txt"), 14 | scalacOptions in(Compile, doc) ++= Seq("-doc-title", name.value), 15 | scalacOptions in(Compile, doc) ++= { 16 | val copy = "© 2014-2016 Michael Ummels" 17 | val footer = name.value + " " + version.value + " API documentation. " + copy 18 | Seq("-doc-footer", footer) 19 | }, 20 | scalacOptions in(Compile, doc) ++= { 21 | val prefix = "https://www.github.com/ummels/scala-prioritymap/blob/" 22 | val path = "/src/main/scala/de/ummels/prioritymap/€{TPL_NAME}.scala" 23 | if (isSnapshot.value) 24 | Seq("-doc-source-url", prefix + "master" + path) 25 | else 26 | Seq("-doc-source-url", prefix + "v" + version.value + path) 27 | }, 28 | autoAPIMappings := true 29 | ) 30 | 31 | lazy val publishSettings = Seq( 32 | publishTo := { 33 | val nexus = "https://oss.sonatype.org/" 34 | if (isSnapshot.value) 35 | Some("snapshots" at nexus + "content/repositories/snapshots") 36 | else 37 | Some("releases" at nexus + "service/local/staging/deploy/maven2") 38 | }, 39 | credentials ++= (for (pw <- sys.env.get("SONATYPE_PASSWORD").toSeq) yield 40 | Credentials("Sonatype Nexus Repository Manager", 41 | "oss.sonatype.org", 42 | "ummels", 43 | pw)), 44 | publishMavenStyle := true, 45 | publishArtifact in Test := false, 46 | pomIncludeRepository := { _ => false }, 47 | homepage := Some(url("https://github.com/ummels/scala-prioritymap")), 48 | licenses := Seq("ISC License" -> url("http://opensource.org/licenses/ISC")), 49 | scmInfo := Some(ScmInfo(url("https://github.com/ummels/scala-prioritymap"), 50 | "scm:git:git@github.com:ummels/scala-prioritymap.git")), 51 | apiURL := { 52 | val baseUrl = "https://oss.sonatype.org/service/local/repositories/releases/archive/de/ummels/" 53 | val artifact = normalizedName.value + "_" + scalaBinaryVersion.value 54 | val dir = artifact + "/" + version.value + "/" 55 | val jar = artifact + "-" + version.value + "-javadoc.jar" 56 | if (isSnapshot.value) Some(url("https://ummels.github.io/scala-prioritymap/api/")) 57 | else Some(url(baseUrl + dir + jar + "/!/")) 58 | }, 59 | pomExtra := { 60 | 61 | 62 | ummels 63 | Michael Ummels 64 | 65 | 66 | }, 67 | pgpSecretRing := file(Path.userHome.absolutePath + "/.sbt/gpg/secring.gpg"), 68 | publishSnapshot := Def.taskDyn { 69 | if (isSnapshot.value) Def.task { publish.value } 70 | else Def.task { 71 | println("Version " + version.value + " is not a snapshot version.") 72 | } 73 | }.value 74 | ) 75 | 76 | lazy val noPublishSettings = Seq( 77 | publish := (), 78 | publishLocal := (), 79 | PgpKeys.publishSigned := (), 80 | publishArtifact := false 81 | ) 82 | 83 | lazy val siteSettings = site.settings ++ ghpages.settings ++ Seq( 84 | git.remoteRepo := { 85 | val repo = "ummels/scala-prioritymap.git" 86 | sys.env.get("GITHUB_TOKEN") match { 87 | case None => "git@github.com:" + repo 88 | case Some(token) => "https://" + token + "@github.com/" + repo 89 | } 90 | } 91 | ) ++ site.includeScaladoc("api") 92 | 93 | lazy val commonSettings = Seq( 94 | parallelExecution in Test := false, 95 | coverageHighlighting := scalaBinaryVersion.value == "2.12", 96 | scalastyleSources in Compile := (unmanagedSourceDirectories in Compile).value // See https://github.com/scalastyle/scalastyle-sbt-plugin/issues/47 97 | ) 98 | 99 | lazy val commonJsSettings = Seq( 100 | scalaJSStage in Global := FastOptStage, 101 | parallelExecution := false 102 | ) 103 | 104 | lazy val commonJvmSettings = Seq( 105 | testOptions in Test += Tests.Argument(TestFrameworks.ScalaTest, "-oDF") 106 | ) 107 | 108 | lazy val root = project.in(file(".")). 109 | settings(name := "scala-prioritymap-root"). 110 | settings(buildSettings:_*). 111 | settings(noPublishSettings:_*). 112 | aggregate(libraryJVM, libraryJS, testsJVM, testsJS) 113 | 114 | lazy val library = crossProject.crossType(CrossType.Pure).in(file("library")). 115 | settings(name := "scala-prioritymap"). 116 | settings(buildSettings:_*). 117 | settings(docSettings:_*). 118 | settings(publishSettings:_*). 119 | settings(commonSettings:_*). 120 | jvmSettings(commonJvmSettings:_*). 121 | jvmSettings(siteSettings:_*). 122 | jsSettings(commonJsSettings:_*). 123 | jsConfigure(_.disablePlugins(scoverage.ScoverageSbtPlugin)) 124 | 125 | lazy val libraryJVM = library.jvm 126 | 127 | lazy val libraryJS = library.js 128 | 129 | lazy val tests = crossProject.in(file("tests")). 130 | dependsOn(library). 131 | settings(name := "scala-prioritymap-tests"). 132 | settings(buildSettings:_*). 133 | settings(noPublishSettings:_*). 134 | settings(commonSettings:_*). 135 | settings( 136 | libraryDependencies += "org.scalatest" %%% "scalatest" % "3.0.1" % "test", 137 | libraryDependencies += "org.scalacheck" %%% "scalacheck" % "1.13.4" % "test" 138 | ). 139 | jvmSettings(commonJvmSettings:_*). 140 | jsSettings(commonJsSettings:_*). 141 | jsConfigure(_.disablePlugins(scoverage.ScoverageSbtPlugin)) 142 | 143 | lazy val testsJVM = tests.jvm 144 | 145 | lazy val testsJS = tests.js 146 | 147 | addCommandAlias("validateJVM", ";libraryJVM/scalastyle;coverage;testsJVM/test;coverageReport") 148 | addCommandAlias("validateJS", ";libraryJS/scalastyle;testsJS/test") 149 | addCommandAlias("validate", ";validateJVM;validateJS") 150 | -------------------------------------------------------------------------------- /library/src/main/scala/de/ummels/prioritymap/PriorityMap.scala: -------------------------------------------------------------------------------- 1 | package de.ummels.prioritymap 2 | 3 | import scala.collection.generic.CanBuildFrom 4 | import scala.collection.immutable._ 5 | 6 | /** A generic trait for immutable priority maps. Concrete classes have to provide 7 | * functionality for the abstract methods in `PriorityMap`: 8 | * 9 | * {{{ 10 | * implicit def ordering: Ordering[B] 11 | * def get(key: A): Option[B] 12 | * def iterator: Iterator[(A, B)] 13 | * def +(kv: (A, B)): PriorityMap[A, B] 14 | * def +[B1 >: B](kv: (A, B1)): Map[A, B1] 15 | * def -(key: A): PriorityMap[A, B] 16 | * def rangeImpl(from: Option[B], until: Option[B]): PriorityMap[A, B] 17 | * }}} 18 | * 19 | * The iterator returned by `iterator` should generate key/value pairs in the 20 | * order specified by the implicit ordering on values. 21 | * 22 | * Concrete classes may also override other methods for efficiency. 23 | */ 24 | trait PriorityMap[A, B] extends Map[A, B] with PriorityMapLike[A, B, PriorityMap[A, B]] { 25 | 26 | import PriorityMap._ 27 | 28 | /** An empty priority map of the same type as this priority map. */ 29 | override def empty: PriorityMap[A, B] = PriorityMap.empty 30 | 31 | override def seq: PriorityMap[A, B] = this 32 | 33 | /** The same priority map with a given default function. 34 | * Note: `get`, `contains`, `iterator`, `keys`, etc. are not affected by `withDefault`. 35 | * 36 | * Invoking transformer methods (e.g. `map`) will not preserve the default value. 37 | * 38 | * @param d the function mapping keys to values, used for non-present keys 39 | * @return a wrapper of this priority map with a default function 40 | */ 41 | def withDefault(d: A => B): PriorityMap[A, B] = new WithDefault(this, d) 42 | 43 | /** The same priority map with a given default value. 44 | * Note: `get`, `contains`, `iterator`, `keys`, etc. are not affected by `withDefaultValue`. 45 | * 46 | * Invoking transformer methods (e.g. `map`) will not preserve the default value. 47 | * 48 | * @param d default value used for non-present keys 49 | * @return a wrapper of this priority map with a default value 50 | */ 51 | def withDefaultValue(d: B): PriorityMap[A, B] = new WithDefault(this, x => d) 52 | 53 | // $COVERAGE-OFF$ 54 | override def stringPrefix: String = "PriorityMap" 55 | // $COVERAGE-ON$ 56 | } 57 | 58 | /** This object provides a set of operations needed to create priority maps. */ 59 | object PriorityMap extends PriorityMapFactory[PriorityMap] { 60 | 61 | import scala.language.implicitConversions 62 | 63 | def empty[A, B](implicit ord: Ordering[B]): PriorityMap[A, B] = StandardPriorityMap.empty[A, B] 64 | 65 | implicit def canBuildFrom[A, B](implicit ord: Ordering[B]): CanBuildFrom[Coll, (A, B), PriorityMap[A, B]] = 66 | new PriorityMapCanBuildFrom[A, B] 67 | 68 | private[prioritymap] trait Default[A, B] extends DefaultMap[A, B] with PriorityMap[A, B] { 69 | override def +(kv: (A, B)): PriorityMap[A, B] = { 70 | val b = newBuilder 71 | b ++= this 72 | b += kv 73 | b.result() 74 | } 75 | 76 | override def -(key: A): PriorityMap[A, B] = { 77 | val b = newBuilder 78 | for (kv <- this; if kv._1 != key) b += kv 79 | b.result() 80 | } 81 | } 82 | 83 | private[prioritymap] class WithDefault[A, B](underlying: PriorityMap[A, B], default: A => B) 84 | extends Map.WithDefault[A, B](underlying, default) 85 | with PriorityMap[A, B] 86 | with PriorityMapLike[A, B, WithDefault[A, B]] { 87 | 88 | implicit def ordering: Ordering[B] = underlying.ordering 89 | 90 | override def empty: WithDefault[A, B] = new WithDefault(underlying.empty, default) 91 | 92 | def +(kv: (A, B)): WithDefault[A, B] = new WithDefault(underlying + kv, default) 93 | 94 | override def -(key: A): WithDefault[A, B] = new WithDefault(underlying - key, default) 95 | 96 | def rangeImpl(from: Option[B], until: Option[B]): WithDefault[A, B] = 97 | new WithDefault(underlying.rangeImpl(from, until), default) 98 | 99 | override def filterKeys(p: A => Boolean): PriorityMap[A, B] = 100 | new WithDefault(underlying.filterKeys(p), default) 101 | 102 | override def withDefault(d: A => B): PriorityMap[A, B] = new WithDefault(underlying, d) 103 | 104 | override def withDefaultValue(d: B): PriorityMap[A, B] = new WithDefault(underlying, x => d) 105 | 106 | /* The following methods are only overridden for efficiency. */ 107 | 108 | override def last: (A, B) = underlying.last 109 | 110 | override def valueSet: SortedSet[B] = underlying.valueSet 111 | 112 | override def tail: WithDefault[A, B] = new WithDefault(underlying.tail, default) 113 | 114 | override def init: WithDefault[A, B] = new WithDefault(underlying.init, default) 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /library/src/main/scala/de/ummels/prioritymap/PriorityMapBuilder.scala: -------------------------------------------------------------------------------- 1 | package de.ummels.prioritymap 2 | 3 | import scala.collection.mutable 4 | 5 | /** The canonical builder for immutable priority maps, using the `+` method 6 | * to add new elements to an empty priority map. 7 | */ 8 | class PriorityMapBuilder[A, B, Coll <: PriorityMap[A, B] with PriorityMapLike[A, B, Coll]](empty: Coll) 9 | extends mutable.MapBuilder[A, B, Coll](empty) { 10 | override def +=(x: (A, B)): this.type = { 11 | elems = elems + x 12 | this 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /library/src/main/scala/de/ummels/prioritymap/PriorityMapFactory.scala: -------------------------------------------------------------------------------- 1 | package de.ummels.prioritymap 2 | 3 | import scala.collection.generic.CanBuildFrom 4 | import scala.collection.mutable 5 | import scala.language.higherKinds 6 | 7 | /** A template for companion objects of PriorityMap and subclasses thereof. */ 8 | abstract class PriorityMapFactory[CC[A, B] <: PriorityMap[A, B] with PriorityMapLike[A, B, CC[A, B]]] { 9 | 10 | type Coll = CC[_, _] 11 | 12 | /** An empty priority map. */ 13 | def empty[A, B](implicit ord: Ordering[B]): CC[A, B] 14 | 15 | /** The standard builder for priority maps. */ 16 | def newBuilder[A, B](implicit ord: Ordering[B]): mutable.Builder[(A, B), CC[A, B]] = 17 | new PriorityMapBuilder[A, B, CC[A, B]](empty[A, B]) 18 | 19 | /** A priority map that contains the given key/value bindings. 20 | * 21 | * @tparam A the key type 22 | * @tparam B the value type 23 | * @param kvs the key/value pairs that make up the map 24 | * @param ord the implicit ordering on values 25 | * @return a new priority map with the given bindings 26 | */ 27 | def apply[A, B](kvs: (A, B)*)(implicit ord: Ordering[B]): CC[A, B] = 28 | (newBuilder[A, B](ord) ++= kvs).result() 29 | 30 | class PriorityMapCanBuildFrom[A, B](implicit ord: Ordering[B]) extends 31 | CanBuildFrom[Coll, (A, B), CC[A, B]] { 32 | def apply(from: Coll): mutable.Builder[(A, B), CC[A, B]] = newBuilder[A, B] 33 | 34 | def apply(): mutable.Builder[(A, B), CC[A, B]] = newBuilder[A, B] 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /library/src/main/scala/de/ummels/prioritymap/PriorityMapLike.scala: -------------------------------------------------------------------------------- 1 | package de.ummels.prioritymap 2 | 3 | import scala.collection.GenTraversableOnce 4 | import scala.collection.immutable._ 5 | 6 | /** A template trait for immutable priority maps. 7 | * To create a concrete priority map, you need to implement the following methods 8 | * in addition to those of `MapLike`: 9 | * 10 | * {{{ 11 | * implicit def ordering: Ordering[B] 12 | * def +(kv: (A, B)): This 13 | * def rangeImpl(from: Option[B], until: Option[B]): This 14 | * }}} 15 | * 16 | * The iterator returned by `iterator` should generate key/value pairs in the 17 | * order specified by the implicit ordering on values. 18 | * 19 | * Concrete classes may also override other methods for efficiency. 20 | */ 21 | trait PriorityMapLike[A, B, +This <: PriorityMapLike[A, B, This] with PriorityMap[A, B]] 22 | extends MapLike[A, B, This] { self => 23 | implicit def ordering: Ordering[B] 24 | 25 | /** Adds a key/value binding to this priority map. 26 | * 27 | * @param kv the key/value pair 28 | * @return a new priority map with the new binding added to this priority map 29 | */ 30 | def +(kv: (A, B)): This 31 | 32 | /** Adds a key/value binding to this priority map. 33 | * 34 | * @param key the key 35 | * @param value the value 36 | * @return a new priority map with the new binding added to this map 37 | */ 38 | def updated(key: A, value: B): This = this + (key -> value) 39 | 40 | /** Adds two or more key/value bindings to this priority map. 41 | * 42 | * @param kv1 the first key/value pair to add 43 | * @param kv2 the second key/value pair to add 44 | * @param kvs the remaining key/value pairs to add 45 | * @return a new priority map with the new bindings added to this map 46 | */ 47 | def +(kv1: (A, B), kv2: (A, B), kvs: (A, B)*): This = 48 | this + kv1 + kv2 ++ kvs 49 | 50 | /** Adds a number of key/value bindings to this priority map. 51 | * 52 | * @param kvs a traversable object consisting of key/value pairs 53 | * @return a new priority map with the new bindings added to this map 54 | */ 55 | def ++(kvs: GenTraversableOnce[(A, B)]): This = 56 | ((repr: This) /: kvs)(_ + _) 57 | 58 | /** Merges a number of key/value bindings into this priority map. 59 | * 60 | * If a key is contained in both this map and the given bindings, computes 61 | * the new value by applying the given merge function to the existing value 62 | * and the new value. 63 | * 64 | * @param kvs a traversable object consisting of key/value pairs 65 | * @param f the merge function 66 | * @return a new priority map with the new bindings merged into this map 67 | */ 68 | def merged(kvs: GenTraversableOnce[(A, B)])(f: (B, B) => B): This = 69 | ((repr: This) /: kvs) { (m, kv) => 70 | val (k, v2) = kv 71 | m.get(k) match { 72 | case None => m + kv 73 | case Some(v1) => m + (k -> f(v1, v2)) 74 | } 75 | } 76 | 77 | /** Transforms this map by applying a function to every retrieved value. 78 | * 79 | * @param f the function used to transform values of this map 80 | * @return a new priority map that maps every key of this map 81 | * to `f(this(key))` 82 | */ 83 | def mapValues[C](f: B => C)(implicit ord: Ordering[C]): PriorityMap[A, C] = 84 | map { case (a, b) => (a, f(b)) } 85 | 86 | /** Returns a new priority map of the same type as this priority map that 87 | * only contains values between the given optional bounds. 88 | * 89 | * @param from the lower-bound (inclusive) on values or 90 | * `None` if there is no lower bound 91 | * @param until the upper-bound (exclusive) on values or 92 | * `None` if there is no upper bound 93 | */ 94 | def rangeImpl(from: Option[B], until: Option[B]): This 95 | 96 | /** Returns a new priority map of the same type as this priority map that 97 | * only contains values greater than or equal to the given lower bound. 98 | * 99 | * @param from the lower-bound (inclusive) on values 100 | */ 101 | def from(from: B): This = rangeImpl(Some(from), None) 102 | 103 | /** Returns a new priority map of the same type as this priority map that 104 | * only contains values smaller than the given upper bound. 105 | * 106 | * @param until the upper-bound (exclusive) on values 107 | */ 108 | def until(until: B): This = rangeImpl(None, Some(until)) 109 | 110 | /** Returns a new priority map of the same type as this priority map that 111 | * only contains values between the given bounds. 112 | * 113 | * @param from the lower-bound (inclusive) on values 114 | * @param until the upper-bound (exclusive) on values 115 | */ 116 | def range(from: B, until: B): This = rangeImpl(Some(from), Some(until)) 117 | 118 | /** Optionally returns the first key of this priority map. */ 119 | def firstKey: Option[A] = headOption map (_._1) 120 | 121 | /** Optionally returns the last key of this priority map. */ 122 | def lastKey: Option[A] = lastOption map (_._1) 123 | 124 | /** Optionally returns the first value of this priority map. */ 125 | def firstValue: Option[B] = headOption map (_._2) 126 | 127 | /** Optionally returns the last value of this priority map. */ 128 | def lastValue: Option[B] = lastOption map (_._2) 129 | 130 | /** Returns the values of this priority map as a sorted set. */ 131 | def valueSet: SortedSet[B] = SortedSet.empty[B] ++ values 132 | 133 | override protected[this] def newBuilder = new PriorityMapBuilder[A, B, This](empty) 134 | 135 | override def filterKeys(p: A => Boolean): PriorityMap[A, B] = 136 | new FilteredKeys(p) with PriorityMap.Default[A, B] { 137 | implicit def ordering: Ordering[B] = self.ordering 138 | 139 | def rangeImpl(from: Option[B], until: Option[B]) = 140 | self.rangeImpl(from, until).filterKeys(p) 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /library/src/main/scala/de/ummels/prioritymap/StandardPriorityMap.scala: -------------------------------------------------------------------------------- 1 | package de.ummels.prioritymap 2 | 3 | import scala.collection.parallel.immutable.ParMap 4 | import scala.collection.generic.CanBuildFrom 5 | import scala.collection.immutable._ 6 | 7 | /** Default implementation of immutable priority maps using a pair of maps. */ 8 | final class StandardPriorityMap[A, B] private (map: Map[A, B], bags: SortedMap[B, Set[A]]) 9 | (implicit val ordering: Ordering[B]) 10 | extends PriorityMap[A, B] 11 | with PriorityMapLike[A, B, StandardPriorityMap[A, B]] 12 | with Serializable { 13 | 14 | def this()(implicit ordering: Ordering[B]) = 15 | this(Map.empty[A, B], SortedMap.empty[B, Set[A]]) 16 | 17 | private def insert(key: A, value: B): StandardPriorityMap[A, B] = { 18 | //require(!map.contains(key)) 19 | val bags1 = bags + (value -> (bags.getOrElse(value, Set.empty) + key)) 20 | new StandardPriorityMap(map.updated(key, value), bags1) 21 | } 22 | 23 | private def delete(key: A, value: B): StandardPriorityMap[A, B] = { 24 | //require(map(key) == value) 25 | val bag = bags(value) - key 26 | val bags1 = if (bag.isEmpty) bags - value else bags updated(value, bag) 27 | new StandardPriorityMap(map - key, bags1) 28 | } 29 | 30 | def +(kv: (A, B)): StandardPriorityMap[A, B] = { 31 | val (key, value) = kv 32 | get(key) match { 33 | case None => insert(key, value) 34 | case Some(`value`) => this 35 | case Some(v) => delete(key, v).insert(key, value) 36 | } 37 | } 38 | 39 | def +[B1 >: B](kv: (A, B1)): Map[A, B1] = map + kv 40 | 41 | def get(key: A): Option[B] = map get key 42 | 43 | def iterator: Iterator[(A, B)] = for { 44 | bag <- bags.valuesIterator 45 | a <- bag.iterator 46 | } yield (a, map(a)) 47 | 48 | def -(key: A): StandardPriorityMap[A, B] = get(key) match { 49 | case None => this 50 | case Some(v) => delete(key, v) 51 | } 52 | 53 | override def empty: StandardPriorityMap[A, B] = StandardPriorityMap.empty 54 | 55 | override def size: Int = map.size 56 | 57 | override def last: (A, B) = { 58 | val key = bags.last._2.last 59 | (key, map(key)) 60 | } 61 | 62 | override def valueSet: SortedSet[B] = bags.keySet 63 | 64 | def rangeImpl(from: Option[B], until: Option[B]): StandardPriorityMap[A, B] = { 65 | val bags1 = bags.rangeImpl(from, until) 66 | val map1 = map.filterKeys(k => (map.get(k) map bags1.contains) getOrElse true) 67 | new StandardPriorityMap[A, B](map1, bags1) 68 | } 69 | 70 | override def tail: StandardPriorityMap[A, B] = headOption match { 71 | case None => throw new UnsupportedOperationException("tail of empty map") 72 | case Some((k, v)) => delete(k, v) 73 | } 74 | 75 | override def init: StandardPriorityMap[A, B] = lastOption match { 76 | case None => throw new UnsupportedOperationException("init of empty map") 77 | case Some((k, v)) => delete(k, v) 78 | } 79 | 80 | override def par: ParMap[A, B] = map.par 81 | } 82 | 83 | /** This object provides a set of operations needed to create priority maps. */ 84 | object StandardPriorityMap extends PriorityMapFactory[StandardPriorityMap] { 85 | 86 | import language.implicitConversions 87 | 88 | def empty[A, B](implicit ord: Ordering[B]): StandardPriorityMap[A, B] = 89 | new StandardPriorityMap[A, B] 90 | 91 | implicit def canBuildFrom[A, B](implicit ord: Ordering[B]): 92 | CanBuildFrom[Coll, (A, B), StandardPriorityMap[A, B]] = 93 | new PriorityMapCanBuildFrom[A, B] 94 | } 95 | -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=0.13.13 2 | -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | resolvers += "jgit-repo" at "http://download.eclipse.org/jgit/maven" 2 | 3 | addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.14") 4 | 5 | addSbtPlugin("org.scalastyle" %% "scalastyle-sbt-plugin" % "0.8.0") 6 | 7 | addSbtPlugin("com.typesafe.sbt" % "sbt-ghpages" % "0.5.4") 8 | 9 | addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.5.0") 10 | 11 | addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.0.0") 12 | -------------------------------------------------------------------------------- /root-doc.txt: -------------------------------------------------------------------------------- 1 | This library provides an implementation of immutable ''priority maps'', i.e. immutable maps that return their entries sorted by value. 2 | 3 | ==Overview== 4 | Priority maps are similar to sorted maps, but while for sorted maps `iterator` returns an iterator that 5 | produces entries sorted by their keys, calling `iterator` on a priority map returns an iterator that 6 | produces entries sorted by their ''values''. Priority maps also offer several `range` methods, which 7 | return a submap with values inside a given range. 8 | 9 | Since calling `head` on a priority map returns a key-value pair with minimal value, priority 10 | maps can also be thought of as a more versatile variant of ''priority queues''. 11 | 12 | ==Usage== 13 | The easiest way to instantiate a new priority map is to use the `apply` method in the 14 | [[de.ummels.prioritymap.PriorityMap]] companion object. 15 | {{{ 16 | scala> val m = PriorityMap('a' -> 1, 'b' -> 2, 'c' -> 0) 17 | m: de.ummels.prioritymap.PriorityMap[Char,Int] = PriorityMap(c -> 0, a -> 1, b -> 2) 18 | }}} 19 | 20 | Since priority maps are immutable, updating a key/value pair returns a new map and does 21 | not modify the old map. 22 | {{{ 23 | scala> m + ('b' -> -1) 24 | res1: de.ummels.prioritymap.PriorityMap[Char,Int] = PriorityMap(b -> -1, c -> 0, a -> 1) 25 | 26 | scala> m 27 | res2: de.ummels.prioritymap.PriorityMap[Char,Int] = PriorityMap(c -> 0, a -> 1, b -> 2) 28 | }}} 29 | 30 | In addition to the methods available for maps, priority maps offer methods for obtaining 31 | a submap whose values lie inside a given range. 32 | {{{ 33 | scala> m.from(1) 34 | res3: de.ummels.prioritymap.PriorityMap[Char,Int] = PriorityMap(a -> 1, b -> 2) 35 | 36 | scala> m.until(2) 37 | res4: de.ummels.prioritymap.PriorityMap[Char,Int] = PriorityMap(c -> 0, a -> 1) 38 | 39 | scala> m.range(1, 2) 40 | res5: de.ummels.prioritymap.PriorityMap[Char,Int] = PriorityMap(a -> 1) 41 | }}} 42 | -------------------------------------------------------------------------------- /scalastyle-config.xml: -------------------------------------------------------------------------------- 1 | 2 | Scalastyle standard configuration 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /tests/js/src/test/scala/de/ummels/prioritymap/PriorityMapSpec.scala: -------------------------------------------------------------------------------- 1 | package de.ummels.prioritymap 2 | 3 | import org.scalatest.PropSpec 4 | 5 | class PriorityMapSpec extends PropSpec with Properties 6 | -------------------------------------------------------------------------------- /tests/jvm/src/test/scala/de/ummels/prioritymap/PriorityMapSpec.scala: -------------------------------------------------------------------------------- 1 | package de.ummels.prioritymap 2 | 3 | import org.scalatest.PropSpec 4 | 5 | class PriorityMapSpec extends PropSpec with ParProperties 6 | -------------------------------------------------------------------------------- /tests/shared/src/test/scala/de/ummels/prioritymap/ParProperties.scala: -------------------------------------------------------------------------------- 1 | package de.ummels.prioritymap 2 | 3 | /** Properties for immutable priority maps that support the `par` operation */ 4 | trait ParProperties extends Properties { 5 | property("par should return an equivalent map") { 6 | forAll(genPriorityMap) { m => 7 | m.par shouldEqual m 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /tests/shared/src/test/scala/de/ummels/prioritymap/Properties.scala: -------------------------------------------------------------------------------- 1 | package de.ummels.prioritymap 2 | 3 | import scala.collection.breakOut 4 | import org.scalacheck.{Arbitrary, Gen} 5 | import org.scalatest.{Inspectors, Matchers, PropSpec, prop} 6 | 7 | import scala.collection.immutable.SortedSet 8 | 9 | /** Properties for immutable priority maps */ 10 | trait Properties extends PropertySpec { 11 | property("apply should create a priority map with the given entries") { 12 | forAll(genOrd, Gen.listOf(genKeyValue)) { (ord, kvs) => 13 | PriorityMap(kvs:_*)(ord) shouldEqual Map(kvs:_*) 14 | StandardPriorityMap(kvs:_*)(ord) shouldEqual Map(kvs:_*) 15 | } 16 | } 17 | 18 | property("size should return the number of elements") { 19 | forAll(genPriorityMap) { m => 20 | m.size shouldBe m.toSeq.size 21 | } 22 | } 23 | 24 | property("iterator should return items in priority order") { 25 | forAll(genPriorityMap) { m => 26 | val values = m.values.toSeq 27 | whenever(values.length > 1) { 28 | Inspectors.forAll(values zip values.tail) { case (v1, v2) => 29 | m.ordering.lteq(v1, v2) shouldBe true 30 | } 31 | } 32 | } 33 | } 34 | 35 | property("head and tail should match") { 36 | forAll(genPriorityMap) { m => 37 | if (m.nonEmpty) { 38 | val (h, t) = (m.head, m.tail) 39 | (h +: t.toSeq) shouldBe m.toSeq 40 | } else { 41 | an [NoSuchElementException] should be thrownBy m.head 42 | an [UnsupportedOperationException] should be thrownBy m.tail 43 | } 44 | } 45 | } 46 | 47 | property("last and init should match") { 48 | forAll(genPriorityMap) { m => 49 | if (m.nonEmpty) { 50 | val (l, i) = (m.last, m.init) 51 | (i.toSeq :+ l) shouldBe m.toSeq 52 | } else { 53 | an [NoSuchElementException] should be thrownBy m.last 54 | an [UnsupportedOperationException] should be thrownBy m.init 55 | } 56 | } 57 | } 58 | 59 | property("+ should behave like ++") { 60 | forAll(genPriorityMap, genKeyValue, Gen.listOf(genKeyValue)) { (m, kv, kvs) => 61 | m + kv shouldBe m ++ Seq(kv) 62 | (m /: kvs)(_ + _) shouldBe m ++ kvs 63 | m + (kv, kv, kvs:_*) shouldBe m ++ (kv :: kvs) 64 | } 65 | } 66 | 67 | property("+ should add or replace entries") { 68 | forAll(genPriorityMap, genKey, genValue) { (m, k, v) => 69 | val m1 = m + (k -> v) 70 | m1(k) shouldBe v 71 | m1 - k shouldBe m - k 72 | } 73 | } 74 | 75 | property("+ should fall back to Map's + for other value types") { 76 | forAll(genPriorityMap, genKey, genValue) { (m, k, v) => 77 | m + (k -> v._1) shouldBe Map(m.toSeq:_*) + (k -> v._1) 78 | } 79 | } 80 | 81 | property("updated should behave like +") { 82 | forAll(genPriorityMap, genKey, genValue) { (m, key, value) => 83 | m updated(key, value) shouldBe m + (key -> value) 84 | } 85 | } 86 | 87 | property("merged should merge old and new values") { 88 | forAll(for { 89 | m <- genPriorityMap 90 | keys <- Gen.listOfN(m.size, genKey) 91 | vals <- Gen.listOfN(m.size, genValue) 92 | f <- Arbitrary.arbitrary[(Value, Value) => Value] 93 | } yield (m, keys, vals, f)) { case (m, keys, vals, f) => 94 | val kvs = keys zip vals 95 | val m1 = (m merged kvs)(f) 96 | val all = m.toSeq ++ kvs 97 | val m2 = all groupBy (_._1) mapValues (kvs => kvs map (_._2) reduceLeft f) 98 | m1 shouldEqual m2 99 | } 100 | } 101 | 102 | property("- should remove elements") { 103 | forAll(genPriorityMap, genKey, genValue) { (m, key, value) => 104 | val m1 = m - key 105 | if (!(m contains key)) { 106 | (m + (key -> value) - key) shouldBe m 107 | m1 shouldBe m 108 | } 109 | else { 110 | m1 should not contain key 111 | m1 + (key -> m(key)) shouldBe m 112 | } 113 | } 114 | } 115 | 116 | property("filterKeys should behave like filter") { 117 | forAll(genPriorityMap, Arbitrary.arbitrary[Key => Boolean]) { (m, p) => 118 | m.filterKeys(p) shouldBe m.filter { case (k, _) => p(k) } 119 | } 120 | } 121 | 122 | property("mapValues should behave like map") { 123 | forAll(genPriorityMap, Arbitrary.arbitrary[Value => Int]) { (m, f) => 124 | m.mapValues(f) shouldBe m.map { case (k, v) => (k, f(v)) } 125 | } 126 | } 127 | 128 | property("firstKey should optionally return the first key") { 129 | forAll(genPriorityMap) { m => 130 | m.firstKey shouldBe m.keys.headOption 131 | } 132 | } 133 | 134 | property("lastKey should optionally return the last key") { 135 | forAll(genPriorityMap) { m => 136 | m.lastKey shouldBe m.keys.lastOption 137 | } 138 | } 139 | 140 | property("firstValue should optionally return the first value") { 141 | forAll(genPriorityMap) { m => 142 | m.firstValue shouldBe m.values.headOption 143 | } 144 | } 145 | 146 | property("lastValue should optionally return the last value") { 147 | forAll(genPriorityMap) { m => 148 | m.lastValue shouldBe m.values.lastOption 149 | } 150 | } 151 | 152 | property("valueSet should return the set of values") { 153 | forAll(genPriorityMap) { m => 154 | val s = m.valueSet 155 | val ord = m.ordering 156 | s shouldBe SortedSet.empty(ord) ++ m.values 157 | s.ordering shouldBe ord 158 | } 159 | } 160 | 161 | property("range should filter out entries with values outside the given bounds") { 162 | forAll(genPriorityMap, genValue, genValue) { (m, from, until) => 163 | val ord = m.ordering 164 | m.range(from, until) shouldBe m.filter { case (_, v) => ord.lteq(from, v) && ord.lt(v, until) } 165 | } 166 | } 167 | 168 | property("from should filter out entries with small values") { 169 | forAll(genPriorityMap, genValue) { (m, from) => 170 | m.from(from) shouldBe m.filter { case (_, v) => m.ordering.lteq(from, v) } 171 | } 172 | } 173 | 174 | property("until should filter out entries with big values") { 175 | forAll(genPriorityMap, genValue) { (m, until) => 176 | m.until(until) shouldBe m.filter { case (_, v) => m.ordering.lt(v, until) } 177 | } 178 | } 179 | 180 | property("withDefault should use the default function for keys not in the map") { 181 | forAll(genPriorityMap, genKey, Arbitrary.arbitrary[Key => Value]) { (m, key, d) => 182 | val m1 = m.withDefault(d) 183 | m1(key) shouldBe m.getOrElse(key, d(key)) 184 | (m1 - key)(key) shouldBe d(key) 185 | m1.empty(key) shouldBe d(key) 186 | m1.filterKeys(_ != key)(key) shouldBe d(key) 187 | } 188 | } 189 | 190 | property("withDefaultValue should use the default function for keys not in the map") { 191 | forAll(genPriorityMap, genKey, genValue) { (m, key, d) => 192 | val m1 = m.withDefaultValue(d) 193 | m1(key) shouldBe m.getOrElse(key, d) 194 | (m1 - key)(key) shouldBe d 195 | m1.empty(key) shouldBe d 196 | m1.filterKeys(_ != key)(key) shouldBe d 197 | } 198 | } 199 | 200 | property("breakOut should be able to yield a builder for priority maps") { 201 | forAll(Gen.listOf(genKey)) { keys => 202 | val m1: PriorityMap[Key, Key] = keys.map(k => k -> k)(breakOut) 203 | m1.keys.toSet shouldBe keys.toSet 204 | val m2: StandardPriorityMap[Key, Key] = keys.map(k => k -> k)(breakOut) 205 | m2.keys.toSet shouldBe keys.toSet 206 | } 207 | } 208 | } 209 | -------------------------------------------------------------------------------- /tests/shared/src/test/scala/de/ummels/prioritymap/PropertySpec.scala: -------------------------------------------------------------------------------- 1 | package de.ummels.prioritymap 2 | 3 | import org.scalacheck.{Arbitrary, Gen} 4 | import org.scalatest.{PropSpecLike, prop, Matchers} 5 | 6 | trait PropertySpec extends PropSpecLike with prop.PropertyChecks with Matchers with prop.Configuration { 7 | override implicit val generatorDrivenConfig: PropertyCheckConfiguration = 8 | PropertyCheckConfiguration(minSuccessful = 100) 9 | 10 | private val ord1 = Ordering.Tuple2(Ordering.Int, Ordering.Int) 11 | private val ord2 = Ordering.by[(Int, Int), Int](x => x._1) 12 | 13 | type Key = Int 14 | type Value = (Int, Int) 15 | 16 | def genOrd: Gen[Ordering[Value]] = Gen.oneOf(ord1, ord2) 17 | 18 | def genKey: Gen[Key] = Gen.choose(-10, 10) 19 | 20 | def genValue: Gen[Value] = Gen.zip(genKey, genKey) 21 | 22 | def genKeyValue: Gen[(Key, Value)] = Gen.zip(genKey, genValue) 23 | 24 | def genPriorityMap: Gen[PriorityMap[Key, Value]] = for { 25 | ord <- genOrd 26 | kvs <- Gen.listOf(genKeyValue) 27 | m = PriorityMap.empty(ord) ++ kvs 28 | default <- Arbitrary.arbitrary[Option[Value]] 29 | pred <- Arbitrary.arbitrary[Option[Key => Boolean]] 30 | } yield (default, pred) match { 31 | case (None, None) => m 32 | case (Some(d), None) => m withDefaultValue d 33 | case (None, Some(p)) => m filterKeys p 34 | case (Some(d), Some(p)) => (m withDefaultValue d) filterKeys p 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /version.sbt: -------------------------------------------------------------------------------- 1 | version in ThisBuild := "1.0.1-SNAPSHOT" 2 | --------------------------------------------------------------------------------