Skip to content

Commit dbf4fc0

Browse files
committed
Change the return type, and add tests
1 parent 0f29382 commit dbf4fc0

File tree

4 files changed

+70
-26
lines changed

4 files changed

+70
-26
lines changed

language-adaptors/rxjava-scala/src/examples/scala/rx/lang/scala/examples/RxScalaDemo.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -870,22 +870,22 @@ class RxScalaDemo extends JUnitSuite {
870870
val o : Observable[String] = List("alice", "bob", "carol", "allen", "clarke").toObservable
871871
val keySelector = (s: String) => s.head
872872
val m = o.toMultimap(keySelector)
873-
println(m.toBlocking.single.mapValues(_.toList))
873+
println(m.toBlocking.single)
874874
}
875875

876876
@Test def toMultimapExample2(): Unit = {
877877
val o : Observable[String] = List("alice", "bob", "carol", "allen", "clarke").toObservable
878878
val keySelector = (s: String) => s.head
879879
val valueSelector = (s: String) => s.tail
880880
val m = o.toMultimap(keySelector, valueSelector)
881-
println(m.toBlocking.single.mapValues(_.toList))
881+
println(m.toBlocking.single)
882882
}
883883

884884
@Test def toMultimapExample3(): Unit = {
885885
val o: Observable[String] = List("alice", "bob", "carol", "allen", "clarke").toObservable
886886
val keySelector = (s: String) => s.head
887887
val valueSelector = (s: String) => s.tail
888-
val mapFactory: () => mutable.Map[Char, mutable.Buffer[String]] = () => mutable.Map('d' -> mutable.ListBuffer("oug"))
888+
val mapFactory = () => mutable.Map('d' -> mutable.Buffer("oug"))
889889
val m = o.toMultimap(keySelector, valueSelector, mapFactory)
890890
println(m.toBlocking.single.mapValues(_.toList))
891891
}
@@ -894,7 +894,7 @@ class RxScalaDemo extends JUnitSuite {
894894
val o : Observable[String] = List("alice", "bob", "carol", "allen", "clarke").toObservable
895895
val keySelector = (s: String) => s.head
896896
val valueSelector = (s: String) => s.tail
897-
val mapFactory: () => mutable.Map[Char, mutable.Buffer[String]] = () => mutable.Map('d' -> mutable.ListBuffer("oug"))
897+
val mapFactory = () => mutable.Map('d' -> mutable.ListBuffer("oug"))
898898
val valueFactory = (k: Char) => mutable.ListBuffer[String]()
899899
val m = o.toMultimap(keySelector, valueSelector, mapFactory, valueFactory)
900900
println(m.toBlocking.single)

language-adaptors/rxjava-scala/src/main/scala/rx/lang/scala/Observable.scala

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3913,47 +3913,52 @@ trait Observable[+T]
39133913
}
39143914

39153915
/**
3916-
* Returns an Observable that emits a single `Map`, returned by a specified mapFactory` function, that
3917-
* contains an `Seq` of values, extracted by a specified `valueSelector` function from items
3918-
* emitted by the source Observable and keyed by the `keySelector` function.
3916+
* Returns an Observable that emits a single `mutable.Map[K, mutable.Buffer[V]]`, returned by a specified `mapFactory` function, that
3917+
* contains values, extracted by a specified `valueSelector` function from items emitted by the source Observable and
3918+
* keyed by the `keySelector` function.
39193919
*
39203920
* <img width="640" height="305" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/toMultiMap.png">
39213921
*
39223922
* @param keySelector the function that extracts a key from the source items to be used as the key in the Map
39233923
* @param valueSelector the function that extracts a value from the source items to be used as the value in the Map
3924-
* @param mapFactory he function that returns a Map instance to be used
3925-
* @return an Observable that emits a single item: a `Map` that contains a `Seq` items mapped from the source
3926-
* Observable
3924+
* @param mapFactory he function that returns a `mutable.Map[K, mutable.Buffer[V]]` instance to be used
3925+
* @return an Observable that emits a single item: a `mutable.Map[K, mutable.Buffer[V]]` that contains items mapped
3926+
* from the source Observable
39273927
*/
3928-
def toMultimap[K, V](keySelector: T => K, valueSelector: T => V, mapFactory: () => mutable.Map[K, mutable.Buffer[V]]): Observable[scala.collection.Map[K, Seq[V]]] = {
3929-
toMultimap(keySelector, valueSelector, mapFactory, k => mutable.ListBuffer[V]())
3928+
def toMultimap[K, V, M <: mutable.Map[K, mutable.Buffer[V]]](keySelector: T => K, valueSelector: T => V, mapFactory: () => M): Observable[M] = {
3929+
toMultimap[K, V, mutable.Buffer[V], M](keySelector, valueSelector, mapFactory, k => mutable.Buffer[V]())
39303930
}
39313931

39323932
/**
3933-
* Returns an Observable that emits a single `Map`, returned by a specified `mapFactory` function, that
3934-
* contains a custom `Seq` of values, extracted by a specified `valueSelector` function from
3935-
* items emitted by the source Observable, and keyed by the `keySelector` function.
3933+
* Returns an Observable that emits a single `mutable.Map[K, B]`, returned by a specified `mapFactory` function, that
3934+
* contains values extracted by a specified `valueSelector` function from items emitted by the source Observable, and
3935+
* keyed by the `keySelector` function.
39363936
*
39373937
* <img width="640" height="305" src="https://raw.github.com/wiki/Netflix/RxJava/images/rx-operators/toMultiMap.png">
39383938
*
39393939
* @param keySelector the function that extracts a key from the source items to be used as the key in the Map
39403940
* @param valueSelector the function that extracts a value from the source items to be used as the value in the Map
39413941
* @param mapFactory the function that returns a Map instance to be used
3942-
* @param collectionFactory the function that returns a Collection instance for a particular key to be used in the Map
3943-
* @return an Observable that emits a single item: a `Map` that contains the `Seq` of mapped items from
3944-
* the source Observable
3942+
* @param collectionFactory the function that returns a `mutable.Buffer[V]` instance for a particular key to be used in the Map
3943+
* @return an Observable that emits a single item: a `mutable.Map[K, B]` that contains mapped items from the source Observable
39453944
*/
3946-
def toMultimap[K, V](keySelector: T => K, valueSelector: T => V, mapFactory: () => mutable.Map[K, mutable.Buffer[V]], collectionFactory: K => mutable.Buffer[V]): Observable[scala.collection.Map[K, Seq[V]]] = {
3945+
def toMultimap[K, V, B <: mutable.Buffer[V], M <: mutable.Map[K, B]](keySelector: T => K, valueSelector: T => V, mapFactory: () => M, collectionFactory: K => B): Observable[M] = {
3946+
// It's complicated to convert `mutable.Map[K, mutable.Buffer[V]]` to `java.util.Map[K, java.util.Collection[V]]`,
3947+
// so RxScala implements `toMultimap` directly.
3948+
// Choosing `mutable.Buffer/Map` is because `append/update` is necessary to implement an efficient `toMultimap`.
39473949
lift {
3948-
(subscriber: Subscriber[scala.collection.Map[K, Seq[V]]]) => {
3949-
val map = mapFactory().withDefault(collectionFactory)
3950+
(subscriber: Subscriber[M]) => {
3951+
val map = mapFactory()
39503952
Subscriber[T](
39513953
subscriber,
39523954
(t: T) => {
39533955
val key = keySelector(t)
3954-
val value = map(key)
3955-
value += valueSelector(t)
3956-
map += key -> value: Unit
3956+
val values = map.get(key) match {
3957+
case Some(v) => v
3958+
case None => collectionFactory(key)
3959+
}
3960+
values += valueSelector(t)
3961+
map += key -> values: Unit
39573962
},
39583963
e => subscriber.onError(e),
39593964
() => {

language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/CompletenessTest.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,8 +177,8 @@ class CompletenessTest extends JUnitSuite {
177177
"timer(Long, Long, TimeUnit)" -> "timer(Duration, Duration)",
178178
"timer(Long, Long, TimeUnit, Scheduler)" -> "timer(Duration, Duration, Scheduler)",
179179
"toList()" -> "toSeq",
180-
"toMultimap(Func1[_ >: T, _ <: K], Func1[_ >: T, _ <: V], Func0[_ <: Map[K, Collection[V]]])" -> "toMultimap(T => K, T => V, () => Map[K, Seq[V]])",
181-
"toMultimap(Func1[_ >: T, _ <: K], Func1[_ >: T, _ <: V], Func0[_ <: Map[K, Collection[V]]], Func1[_ >: K, _ <: Collection[V]])" -> "toMultimap(T => K, T => V, () => Map[K, Seq[V]], K => Seq[V])",
180+
"toMultimap(Func1[_ >: T, _ <: K], Func1[_ >: T, _ <: V], Func0[_ <: Map[K, Collection[V]]])" -> "toMultimap(T => K, T => V, () => M)",
181+
"toMultimap(Func1[_ >: T, _ <: K], Func1[_ >: T, _ <: V], Func0[_ <: Map[K, Collection[V]]], Func1[_ >: K, _ <: Collection[V]])" -> "toMultimap(T => K, T => V, () => M, K => B)",
182182
"toSortedList()" -> "[Sorting is already done in Scala's collection library, use `.toSeq.map(_.sorted)`]",
183183
"toSortedList(Func2[_ >: T, _ >: T, Integer])" -> "[Sorting is already done in Scala's collection library, use `.toSeq.map(_.sortWith(f))`]",
184184
"window(Observable[U])" -> "window(=> Observable[Any])",

language-adaptors/rxjava-scala/src/test/scala/rx/lang/scala/ObservableTest.scala

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package rx.lang.scala
1717

18+
import scala.collection.mutable
1819
import scala.collection.mutable.ListBuffer
1920
import scala.concurrent.{Future, Await}
2021
import scala.concurrent.duration.Duration
@@ -228,4 +229,42 @@ class ObservableTests extends JUnitSuite {
228229
val o = Observable.empty.orElse(-1)
229230
assertEquals(List(-1), o.toBlocking.toList)
230231
}
232+
233+
@Test
234+
def testToMultimap() {
235+
val o = Observable.items("a", "b", "cc", "dd").toMultimap(_.length)
236+
val expected = Map(1 -> List("a", "b"), 2 -> List("cc", "dd"))
237+
assertEquals(expected, o.toBlocking.single)
238+
}
239+
240+
@Test
241+
def testToMultimapWithValueSelector() {
242+
val o = Observable.items("a", "b", "cc", "dd").toMultimap(_.length, s => s + s)
243+
val expected = Map(1 -> List("aa", "bb"), 2 -> List("cccc", "dddd"))
244+
assertEquals(expected, o.toBlocking.single)
245+
}
246+
247+
@Test
248+
def testToMultimapWithMapFactory() {
249+
val m = mutable.Map[Int, mutable.Buffer[String]]()
250+
val o = Observable.items("a", "b", "cc", "dd").toMultimap(_.length, s => s, () => m)
251+
val expected = Map(1 -> List("a", "b"), 2 -> List("cc", "dd"))
252+
val r = o.toBlocking.single
253+
assertTrue(m eq r) // check same instance
254+
assertEquals(expected, r)
255+
}
256+
257+
@Test
258+
def testToMultimapWithCollectionFactory() {
259+
val m = mutable.Map[Int, mutable.Buffer[String]]()
260+
val ls = List(mutable.Buffer[String](), mutable.Buffer[String]())
261+
val o = Observable.items("a", "b", "cc", "dd").toMultimap(_.length, s => s, () => m, (i: Int) => ls(i - 1))
262+
val expected = Map(1 -> List("a", "b"), 2 -> List("cc", "dd"))
263+
val r = o.toBlocking.single
264+
assertTrue(m eq r) // check same instance
265+
assertTrue(ls(0) eq r(1)) // check same instance
266+
assertTrue(ls(1) eq r(2)) // check same instance
267+
assertEquals(expected, r)
268+
}
269+
231270
}

0 commit comments

Comments
 (0)