Showing posts with label filter. Show all posts
Showing posts with label filter. Show all posts
Thursday, May 27, 2010
zipWithIndex
A common desire is to have access to the index of an element when using collection methods like foreach, filter, foldLeft/Right, etc... Fortunately there is a simple way.
But wait!
Does that not trigger an extra iteration through the collection?. Indeed it does and that is where Views help.
When using a view the collection is only traversed when required so there is no performance loss.
Here are some examples of zipWithIndex:
List('a','b','c','d').zipWithIndex
. But wait!
Does that not trigger an extra iteration through the collection?. Indeed it does and that is where Views help.
List('a','b','c','d').view.zipWithIndex
When using a view the collection is only traversed when required so there is no performance loss.
Here are some examples of zipWithIndex:
- scala> val list = List('a','b','c','d')
- list: List[Char] = List(a, b, c, d)
- /*
- I like to use functions constructed with case statements
- in order to clearly label the index. The alternative is
- to use x._2 for the index and x._1 for the value
- */
- scala> list.view.zipWithIndex foreach {case (value,index) => println(value,index)}
- (a,0)
- (b,1)
- (c,2)
- (d,3)
- // alternative syntax without case statement
- scala> list.view.zipWithIndex foreach {e => println(e._1,e._2)}
- (a,0)
- (b,1)
- (c,2)
- (d,3)
- /*
- Fold left and right functions have 2 parameters (accumulator, nextValue)
- using a case statement allows you to expand that but watch the brackets!
- */
- scala> (list.view.zipWithIndex foldLeft 0) {case (acc,(value,index)) => acc + value.toInt + index}
- res14: Int = 400
- // alternative syntax without case statement
- scala> (list.view.zipWithIndex foldLeft 0) {(acc,e) => acc + e._1.toInt + e._2}
- res23: Int = 400
- /*
- alternative foldLeft operator. The thing I like about this
- syntax is that it has the initial accumulator value on the left
- in the same position as the accumulator parameter in the function.
- The other thing I like about it is that visually you can see that it starts with
- "" and the folds left
- */
- scala> ("" /: list.view.zipWithIndex) {
- | case (acc, (value, index)) if index % 2 == 0 => acc + value
- | case (acc, _) => acc
- | }
- res15: java.lang.String = ac
- /*
- This example filters based on the index then uses map to remove the index
- force simply forces the view to be processed. (I love these collections!)
- */
- scala> list.view.zipWithIndex.filter { _._2 % 2 == 0 }.map { _._1}.force
- res29: Seq[Char] = List(a, c)
Thursday, April 29, 2010
Filter with FlatMap (or collect)
I picked up this tip from one of Daniel Spiewak's tweets. He tweeted a pro tip that uses flatMap to create a filtered list:
At a glance one might wonder why not simply use list.filter{_.isInstanceOf[String]}. The difference is that the flatMap will return a List[String].
However Scala 2.8 offers the collect method for doing a similar thing.
- list flatMap {
- case st: String => Some(st)
- case _ => None
- }
At a glance one might wonder why not simply use list.filter{_.isInstanceOf[String]}. The difference is that the flatMap will return a List[String].
However Scala 2.8 offers the collect method for doing a similar thing.
- def strings(list: List[Any]) = list flatMap {
- case st: String => Some(st)
- case _ => None
- }
- // returned list is a List[String]
- scala> strings("hi" :: 1 :: "world" :: 4 :: Nil)
- res11: List[String] = List(hi, world)
- // returned list is a List[Any] (not as useful)
- scala> "hi" :: 1 :: "world" :: 4 :: Nil filter {_.isInstanceOf[String]}
- res12: List[Any] = List(hi, world)
- // collect returns List[String]
- scala> "hi" :: 1 :: "world" :: 4 :: Nil collect {case s:String => s}
- res13: List[String] = List(hi, world)
Tuesday, January 26, 2010
Guard Sugar
This topic is a simple tip for a cleaner syntax for guards. Guards/filters are statements in for-comprehensions and case statements that guard or filter matches. Often you will see them as follows:
However you have the option to apply a little sugar to the statements cleaning them up a little:
As you can see the brackets are optional. That is because in both cases the brackets are not required for the parser to determine the start and end of the guard statements. They are added so that the "normal" syntax used in the typical if statements will compile.
- scala> for (i <- 1 to 10; if (i % 2 == 1)) yield i
- res0: scala.collection.immutable.IndexedSeq[Int] = IndexedSeq(1, 3, 5, 7, 9)
- scala> util.Random.nextInt(10) match {
- | case i if(i>5) => println("hi")
- | case _ => println("low")
- | }
- low
However you have the option to apply a little sugar to the statements cleaning them up a little:
- scala> for (i <- 1 to 10; if i % 2 == 1) yield i
- res2: scala.collection.immutable.IndexedSeq[Int] = IndexedSeq(1, 3, 5, 7, 9)
- scala> util.Random.nextInt(10) match {
- | case i if i>5 => println("hi")
- | case _ => println("low")
- | }
- hi
As you can see the brackets are optional. That is because in both cases the brackets are not required for the parser to determine the start and end of the guard statements. They are added so that the "normal" syntax used in the typical if statements will compile.
- scala> 10 match { case i if i == 1 || i == 10 => "obviously this is a match"}
- res4: java.lang.String = obviously this is a match
- /*
- In case statements you can split the if almost any way you want because it is very clearly bound by the if and the => that is required for all case statements with a guard
- */
- scala> 10 match { case i if i == 1 ||
- | i == 10 => "obviously this is a match"}
- res5: java.lang.String = obviously this is a match
- scala> 10 match {
- | case i
- | if
- | i == 1
- | ||
- | i == 10
- | => "matched"
- | }
- res6: java.lang.String = matched
- /*
- For-comprehensions are not as flexible since it is harder for the compiler to determine where the guard ends. So try to keep it on one line or otherwise make it a function. That is probably good advice for case statements as well.
- */
- scala> for {
- | x <- 1 to 10
- | if
- | x > 10
- | || x == 2 } yield x
- < console>:5: error: '<-' expected but integer literal found.
- || x == 2 } yield x
- ^
- < console>:5: error: illegal start of simple expression
- || x == 2 } yield x
- ^
- scala> for {
- | x <- 1 to 10
- | if x == 1 || x == 10 } yield x
- res7: scala.collection.immutable.IndexedSeq[Int] = IndexedSeq(1, 10)
Labels:
beginner,
filter,
for,
for-comprehension,
guard,
match,
matching,
Scala,
syntactic sugar
Subscribe to:
Comments (Atom)