Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor toMap #67

Merged
merged 5 commits into from
Dec 4, 2014
Merged

Refactor toMap #67

merged 5 commits into from
Dec 4, 2014

Conversation

zsxwing
Copy link
Member

@zsxwing zsxwing commented Nov 20, 2014

A PR to refactor toMap.

There is a breaking change in this PR:

  • Remove def toMap[K, V] (keySelector: T => K, valueSelector: T => V, mapFactory: () => Map[K, V]): Observable[Map[K, V]]

The other changes are still source compatible and binary compatible.

@zsxwing zsxwing mentioned this pull request Nov 20, 2014
@samuelgruetter
Copy link
Collaborator

I like the idea, but please give me some time to check/think about the details ;-)

Moreover, I'd like to know if anything of this PR is a breaking change. It would be nice to have a comparison of what {works/doesn't work} {with/without} this PR. Could then also be copied to the release notes of the first version containg these changes.

}

def toTreeMap[K](keySelector: T => K)(implicit ord: Ordering[K]): Observable[scala.collection.immutable.TreeMap[K, T]] = {
toMap[K, T, scala.collection.immutable.TreeMap](keySelector, v => v)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

scala.collection.immutable.TreeMap ---> immutable.TreeMap

@zsxwing zsxwing changed the title [WIP] Refactor toMap Refactor toMap Nov 24, 2014
@zsxwing
Copy link
Member Author

zsxwing commented Nov 24, 2014

I updated the description and override the PR with a formal commit. Sorry that I should have done it at first.

@zsxwing
Copy link
Member Author

zsxwing commented Nov 24, 2014

having a default value for an implicit seems a bit dangerous at first sight.

You're right. Thanks for pointing out it. I created a helper method

def to[K, V, M[_, _]](keySelector: T => K, valueSelector: T => V)(implicit cbf: CanBuildFrom[Nothing, (K, V), M[K, V]]): Observable[M[K, V]] = {

and used to implement toMap. This will make toXXXMap be consistent with toList, toSeq.

@samuelgruetter
Copy link
Collaborator

Btw did you try out if you can implement toMap just as

this.map(elem => (keySelector(elem), valueSelector(elem))).to[Map]

@zsxwing
Copy link
Member Author

zsxwing commented Nov 25, 2014

It won't work. Here is the signature of to:

def to[Col[_]](implicit cbf: CanBuildFrom[Nothing, T, Col[T @uncheckedVariance]]): Observable[Col[T @uncheckedVariance]]

It expects a Col with one type parameter and will return Col[T].
this.map(elem => (keySelector(elem), valueSelector(elem))).to[Map] won't work because:

  • Map has two type parameters
  • toMap should return Observable[Map[K, V]] instead of Observable[Map[(K, V)]]

@samuelgruetter
Copy link
Collaborator

I see.
Something else: Let's add the toMap of Scala Lists:

def toMap[T, U](implicit ev: A <:< (T, U)): Map[T, U] 

val m = o.toMap(keySelector, valueSelector, mapFactory)
println(m.toBlocking.single)
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you remove this example, you should show how we can do this now. Not necessarily with a non-empty starting map, but maybe with a HashMap from the collection library?

@zsxwing
Copy link
Member Author

zsxwing commented Nov 25, 2014

Let's add the toMap of Scala Lists:

Done

No need to clutter the docs with immutable. prefixes

Done

If you remove this example, you should show how we can do this now.

Added two examples to show how to use to[K, V, M[_, _]]. However, I cannot find a way to make the compiler infer the K, V types.

I'm not sure if we should have these toXxxMap methods.

Maybe it's better not to add toXxxMap methods in this PR?

@samuelgruetter
Copy link
Collaborator

Maybe it's better not to add toXxxMap methods in this PR?

Yes, I think so.

val o : Observable[String] = List("alice", "bob", "carol").toObservable
val keySelector = (s: String) => s.head
val valueSelector = (s: String) => s.tail
val m = o.to[Char, String, HashMap](keySelector, valueSelector)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't quite like these two examples because none of them shows explicitly what's happening. I'd only make one example, but add all types there:

    val m: Observable[HashMap[Char, String]] = o.to[Char, String, HashMap](keySelector, valueSelector)

IMHO that's more readable.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And while I'm reading this, I wonder why we don't swap the order of the type params:

o.to[HashMap, Char, String](keySelector, valueSelector)

corresponds better to the order of pronouncing what we're doing: we say "convert o to a HashMap of Char/String".

@samuelgruetter
Copy link
Collaborator

We should not forget to make toMultimap consistent with toMap, but we can also do that in a separate PR.

@zsxwing
Copy link
Member Author

zsxwing commented Nov 27, 2014

swap the order of the type params

Sounds great. Done.

@zsxwing
Copy link
Member Author

zsxwing commented Nov 27, 2014

I cannot figure out how to refactor toMultimap now. Here is my attempt. However, the problem is how to add value to b?

  def to[M[_, _], Col[_], K, V](keySelector: T => K, valueSelector: T => V)
                               (implicit mcbf: CanBuildFrom[Nothing, (K, Col[V]), M[K, Col[V]]], 
                                cbf: CanBuildFrom[Nothing, V, Col[V]]): Observable[M[K, Col[V]]] = {
    lift {
      (subscriber: Subscriber[M[K, Col[V]]]) => {
        new Subscriber[T](subscriber) {
          val b = mcbf()

          override def onStart(): Unit = request(Long.MaxValue)

          override def onNext(t: T): Unit = {
            val key = keySelector(t)
            val value = valueSelector(t)
            // how to add value to b?
            // b expects (K, Col[V])
          }

          override def onError(e: Throwable): Unit = {
            subscriber.onError(e)
          }

          override def onCompleted(): Unit = {
            subscriber.onNext(b.result)
            subscriber.onCompleted()
          }
        }
      }
    }
  }

@samuelgruetter
Copy link
Collaborator

We could also use scala.collection.mutable.MultiMap, which has an addBinding method. Since it's mutable, there's no need to use a builder, we could just start with an empty mutable multimap and add one key/value pair after the other using addBinding. The question is, however, to what extent we can/want to abstract over the Map type and Set type used by MultiMap.

@zsxwing
Copy link
Member Author

zsxwing commented Nov 28, 2014

Sent another PR #68 for toMultimap as per your suggestion.

* @return an Observable that emits a single item: an `Map` containing the mapped items from the source
* Observable
*/
def toMap[K, V](keySelector: T => K, valueSelector: T => V): Observable[Map[K, V]] = {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you try if you can define a default argument of v => v for valueSelector? Not sure if this can work because scalac would have to infer from this that T must be the same as V.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not work...

@zsxwing
Copy link
Member Author

zsxwing commented Dec 4, 2014

@samuelgruetter further suggestion?

@samuelgruetter
Copy link
Collaborator

LGTM

zsxwing added a commit that referenced this pull request Dec 4, 2014
@zsxwing zsxwing merged commit 2d24264 into ReactiveX:0.x Dec 4, 2014
@zsxwing zsxwing deleted the map branch December 4, 2014 15:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants