Skip to content

Commit

Permalink
modified my non-empty contributions to remove (hopefully) scala 2.13 … (
Browse files Browse the repository at this point in the history
#921)

* modified my non-empty contributions to remove (hopefully) scala 2.13 dependency

* modified my non-empty contributions to remove (hopefully) scala 2.13 dependency

* deleted some comments, changed to final class

* ran fix
  • Loading branch information
TimPigden authored May 29, 2022
1 parent 1438b2a commit d38cadb
Show file tree
Hide file tree
Showing 4 changed files with 674 additions and 0 deletions.
224 changes: 224 additions & 0 deletions core/shared/src/main/scala/zio/prelude/NonEmptyMap.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
package zio.prelude

/*
* Copyright 2020-2022 John A. De Goes and the ZIO Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import zio.NonEmptyChunk

import scala.language.implicitConversions

/**
* A non-empty wrapper for the scala immutable map. Note - this does not attempt to implement all features of
* map but what the author considers to be the "normal ones".
*/
final class NonEmptyMap[K, V] private (private val map: Map[K, V]) { self =>

private def newMap[V2](map: Map[K, V2]): NonEmptyMap[K, V2] = new NonEmptyMap(map)

/** Converts this `NonEmptyMap` to a `Map`. */
def toMap: Map[K, V] = map

/**
* Returns an element of this `NonEmptyMap` and the remainder, which is a (possibly empty) `Map`.
*/
@inline
def peel: ((K, V), Map[K, V]) = (map.head, map.tail)

/**
* Returns an element of this `NonEmptyMap`
* and the remainder or `None`, if the remainder is empty.
*/
def peelNonEmpty: ((K, V), Option[NonEmptyMap[K, V]]) = {
val (head, tail) = peel
if (tail.isEmpty)
(head, None)
else
(head, Some(newMap(tail)))
}

/**
* Creates a new `NonEmptyMap` with an additional element, unless the element is
* already present.
*
* @param elem the element to be added
* @return a new map that contains all elements of this map and that also
* contains `elem`.
*/
def +(elem: (K, V)): NonEmptyMap[K, V] = newMap(map + elem)

/**
* Creates a new `NonEmptyMap` by adding all elements contained in another collection to this `NonEmptyMap`, omitting duplicates.
*
* This method takes a collection of elements and adds all elements, omitting duplicates, into `NonEmptyMap`.
*
* Example:
* {{{
* scala> val a = NonEmptyMap(1, 2) ++ NonEmptyMap(2, "a")
* a: zio.prelude.NonEmptyMap[Any] = NonEmptyMap(1, 2, a)
* }}}
*
* @param elems the collection containing the elements to add.
* @return a new `NonEmptyMap` with the given elements added, omitting duplicates.
*/
def ++(elems: Iterable[(K, V)]): NonEmptyMap[K, V] = newMap(map ++ elems)

/** Adds the `elem` to this `NonEmptyMap`. Alias for `+`. */
def add(elem: (K, V)): NonEmptyMap[K, V] = self + elem

/** Removes the `elem` from this `NonEmptyMap`. Alias for `-`. */
def remove(elem: K): Map[K, V] = map - elem

/**
* Returns the tail of this `NonEmptyMap` if it exists or `None` otherwise.
*/
def tailNonEmpty: Option[NonEmptyMap[K, V]] = peelNonEmpty._2

/**
* Produces a new non empty map where values mapped according to function f. For compatibility
* does not use map.iew
*/
def mapValues[V1](f: V => V1): NonEmptyMap[K, V1] = {
val newInner = map.map(tup => tup._1 -> f(tup._2))
newMap(newInner)
}

override def hashCode: Int = map.hashCode ^ NonEmptyMap.NonEmptyMapSeed

override def equals(that: Any): Boolean =
that match {
case that: AnyRef if self.eq(that) => true
case that: NonEmptyMap[_, _] => self.map == that.toMap
case _ => false
}

override def toString: String = s"NonEmpty$map"
}

object NonEmptyMap {

def apply[K, V](elem: (K, V), others: Iterable[(K, V)]): NonEmptyMap[K, V] =
new NonEmptyMap(others.toMap + elem)

/**
* Creates a `NonEmptyMap` with the specified elements.
* @tparam A the type of the `NonEmptyMap`'s elements
* @param elem an element of the created `NonEmptyMap`
* @param others the remaining elements of the created `NonEmptyMap`
* @return a new `NonEmptyMap` with elements `elem` and `others`
*/
def apply[K, V](elem: (K, V), others: (K, V)*): NonEmptyMap[K, V] = apply(elem, others)

def unapply[K, V](arg: NonEmptyMap[K, V]): Some[((K, V), Map[K, V])] = Some(arg.peel)

/**
* Constructs a `NonEmptyMap` from a `NonEmptyChunk`.
*/
def fromNonEmptyChunk[K, V](elems: NonEmptyChunk[(K, V)]): NonEmptyMap[K, V] = apply(elems.head, elems.tail)

/**
* Constructs a `NonEmptyMap` from a `NonEmptyList`.
*/
def fromNonEmptyList[K, V](elems: NonEmptyList[(K, V)]): NonEmptyMap[K, V] = apply(elems.head, elems.tail)

/**
* Constructs a `NonEmptyMap` from an element and `Map`.
*/
def fromMap[K, V](elem: (K, V), others: Map[K, V]): NonEmptyMap[K, V] = apply(elem, others)

/**
* Constructs a `NonEmptyMap` from a `Map` or `None` otherwise.
*/
def fromMapOption[K, V](map: Map[K, V]): Option[NonEmptyMap[K, V]] = map.headOption.map(fromMap(_, map.tail))

/**
* Constructs a `NonEmptyMap` from an element and `Iterable`.
*/
def fromIterable[K, V](head: (K, V), tail: Iterable[(K, V)]): NonEmptyMap[K, V] =
fromMap(head, tail.toMap)

/**
* Constructs a `NonEmptyMap` from an `Iterable` or `None` otherwise.
*/
def fromIterableOption[K, V](iterable: Iterable[(K, V)]): Option[NonEmptyMap[K, V]] =
iterable.headOption.fold(None: Option[NonEmptyMap[K, V]])(h => Some(fromIterable(h, iterable.tail)))

/**
* Constructs a `NonEmptyMap` with the specified single value.
*/
def single[K, V](head: (K, V)): NonEmptyMap[K, V] =
NonEmptyMap(head)

/**
* Provides an implicit conversion from `NonEmptyMap` to the `Map`
* for interoperability with Scala's collection library.
*/
implicit def toMap[K, V](nonEmptyMap: NonEmptyMap[K, V]): Map[K, V] =
nonEmptyMap.toMap

private val NonEmptyMapSeed: Int = 1247820194

/**
* GroupByOption function returns an option of a nonEmpty map instead of a map because by definition
* the elements will be non-empty - returns None if from is
*/
def groupByOption[A, K](from: Iterable[A])(f: A => K): Option[NonEmptyMap[K, Iterable[A]]] =
from.headOption.map(_ => new NonEmptyMap(from.groupBy(f)))

/**
* from a non-empty chunk we can create a non-empty map of non-empty chunks
*/
def groupByFromNonEmptyChunk[A, K](from: NonEmptyChunk[A])(f: A => K): NonEmptyMap[K, NonEmptyChunk[A]] = {
val gb = from.groupBy(f)
val asChunks = gb.map(p => (p._1 -> NonEmptyChunk.fromIterableOption(p._2).get)) // safe!
new NonEmptyMap(asChunks)
}

/**
* from a non-empty set we can create a non-empty map of non-empty sets
*/
def groupByFromNonEmptySet[A, K](from: NonEmptySet[A])(f: A => K): NonEmptyMap[K, NonEmptySet[A]] = {
val gb = from.groupBy(f)
val asSets = gb.map(p => (p._1 -> NonEmptySet.fromIterableOption(p._2).get)) // safe!
new NonEmptyMap(asSets)
}

/**
* from a non-empty list we can create a non-empty map of non-empty list
*/
def groupByFromNonEmptyList[A, K](from: NonEmptyList[A])(f: A => K): NonEmptyMap[K, NonEmptyList[A]] = {
val gb = from.groupBy(f)
val asLists = gb.map(p => (p._1 -> NonEmptyList.fromIterableOption(p._2).get)) // safe!
new NonEmptyMap(asLists)
}

}

trait NonEmptyMapSyntax {
implicit final class NonEmptyMapIterableOps[K, V](private val iterable: Iterable[(K, V)]) {

/**
* Constructs a `NonEmptyMap` from an `Iterable` or `None` otherwise.
*/
def toNonEmptyMap: Option[NonEmptyMap[K, V]] = NonEmptyMap.fromIterableOption(iterable)
}
implicit final class NonEmptyMapMapOps[K, V](self: Map[K, V]) {

/**
* Constructs a `NonEmptyMap` from a `Map` or `None` otherwise.
*/
def toNonEmptyMap: Option[NonEmptyMap[K, V]] = NonEmptyMap.fromMapOption(self)
}
}
9 changes: 9 additions & 0 deletions core/shared/src/main/scala/zio/prelude/NonEmptySet.scala
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,15 @@ final class NonEmptySet[A] private (private val set: Set[A]) { self =>
/** Removes the `elem` from this `NonEmptySet`. Alias for `-`. */
def remove(elem: A): Set[A] = set - elem

/**
* removes the elem from `NonEmptySet`, returning Some(NonEmptySet) if there's anything
* left, otherwise None
*/
def removeNonEmpty(elem: A): Option[NonEmptySet[A]] = {
val newSet = set - elem
if (newSet.nonEmpty) Some(new NonEmptySet(set)) else None
}

/**
* Returns the tail of this `NonEmptySet` if it exists or `None` otherwise.
*/
Expand Down
Loading

0 comments on commit d38cadb

Please sign in to comment.