views:

194

answers:

5

I am new to Scala and but very old to Java and had some understanding working with FP languages like "Haskell".

Here I am wondering how to implement this using Scala. There is a list of elements in an array all of them are strings and I just want to know if there is a way I can do this in Scala in a FP way. Here is my current version which works...

def checkLength(vals: Array[String]): Boolean = {
  var len = -1
  for(x <- conts){
    if(len < 0)
      len = x.length()
    else{
      if (x.length() != len)
        return false
      else
        len = x.length()
    }
  }
  return true;
}

And I am pretty sure there is a better way of doing this in Scala/FP...

+2  A: 

Tip: Use forall to determine whether all elements in the collection do satisfy a certain predicate (e.g. equality of length).

Dario
+9  A: 
list.forall( str => str.size == list(0).size )

Edit: Here's a definition that's as general as possilbe and also allows to check whether a property other than length is the same for all elements:

def allElementsTheSame[T,U](f: T => U)(list: Seq[T]) = {
    val first: Option[U] = list.headOption.map( f(_) )
    list.forall( f(_) == first.get ) //safe to use get here!
}

type HasSize = { val size: Int }
val checkLength = allElementsTheSame((x: HasSize) => x.size)_

checkLength(Array( "123", "456") )

checkLength(List( List(1,2), List(3,4) ))
sepp2k
+1 Yep. Couldn't the lattern term even just be `list forall (_.size == list(0).size)` ?
Dario
@Dario: Yes, it could.
sepp2k
Excellent. After some trails, I ended up doing this... `def checkLenght(vals: Array[String]): Boolean = vals.forall(_.length == vals(0).length)`
Teja Kantamneni
@Teja Kantamneni: Note that if you leave out the check for the size, you'll get an error when applying the method to empty arrays. Also note that if you declare vals as `Seq[String]` instead of `Array[String]`, it will also work for Lists etc., not just Arrays. (You could also generalize it to work on sequences of anything that has a length method, instead of just strings).
sepp2k
@sepp2k: Well, It should not throw any error as you will go into the loop only if there is an element. Actually I am really new to Scala world and I just started learning it last weekend. I really appreciate your help.
Teja Kantamneni
Your `list.size == 0 ||` is redundant so I've removed it
oxbow_lakes
But your generalization in the `allElementsTheSame` method would of course fail for an empty list. You probably want to use `headOption`
oxbow_lakes
@oxbow_lakes: Is `headOption` equal to Haskell `head`??
Teja Kantamneni
No - `headOption` returns `None` on an empty sequence or `Some(head)` in the case of a non-empty list. It's fixed now
oxbow_lakes
oxbow_lakes: Good points. I was stupid about the size check. Especially in the general method where I checked the size *after* accessing the first element.
sepp2k
On of the things you'll find about Scala is that the language is so concise, adding such "helper" methods is usually unnecessary - it doesn't add anything to the conciseness or readability of the resultant code. In Java-land, you end up writing so much extraneous stuff! Scala tool support may not quite be up to the level of Java's but this doesn't make you any less productive!
oxbow_lakes
I see I'll have to be way more active on Stack Overflow if I'm ever to have fun writing Scala answers again... :-)
Daniel
+1  A: 

If you know that your lists are always non-empty, a straight forall works well. If you don't, it's easy to add that in:

list match {
  case x :: rest => rest forall (_.size == x.size)
  case _ => true
}

Now lists of length zero return true instead of throwing exceptions.

Rex Kerr
Is pattern matching possible on arrays?
Dario
@Dario - yes, they match against sequences. This behaviour has changed slightly in 2.8 but they can still be used in pattern-matching
oxbow_lakes
@Dario - Yes. If "list" is an array, use `case Array(x,rest @ _*) =>` instead. This works in both 2.7 and 2.8.
Rex Kerr
+1  A: 

Here's another approach:

def check(list:List[String]) = list.foldLeft(true)(_ && list.head.length == _.length)
Germán
`forall` approach is more efficient.
F0RR
+2  A: 

Since everyone seems to be so creative, I'll be creative too. :-)

def checkLength(vals: Array[String]): Boolean = vals.map(_.length).removeDuplicates.size <= 1

Mind you, removeDuplicates will likely be named distinct on Scala 2.8.

Daniel
Clever! But until it's renamed, `(Set() ++ list.map(_.length)).size <= 1` is even shorter.
Rex Kerr