2

Need some help with Scala flatten.

I have a list of String and List[String].

Example: List("I", "can't", List("do", "this"))

Expecting result: List("I", "can't", "do", "this")

I've done a lot of experiments, and most compact solution is:

val flattenList = list.flatten {
  case list: List[Any] => list
  case x => List(x)
}

But it seems very tricky and hard to understand. Any suggestions for more naive code?

Thanks.

6
  • stackoverflow.com/questions/1737452/… Commented Oct 31, 2016 at 9:09
  • Did you try list.flatten. If so, why it didn't work for you? Commented Oct 31, 2016 at 9:41
  • @maasg: flatten will not work, because list contains different data types, both String and List[String] Commented Oct 31, 2016 at 10:03
  • I think @antonkw want to know how this could work. flatten assumes that list contains another list. So, case list just uses as it is, and case x converts x to list. flatten example in docs will be helpful: scala-lang.org/api/current/#scala.collection.immutable.List Commented Oct 31, 2016 at 10:05
  • 1
    @antonkw why do you need this? The issue with this approach is that List[Any] might be anything, so you're loosing type-safety. Could the creation point of this list be changed so that the Strings are wrapped in a List? Like this: List(List("I"), List("can't"), List("do", "this")) instead? Commented Oct 31, 2016 at 11:12

2 Answers 2

3

What's "tricky and hard to understand" is your mixing elements of different type in the same list. That's the root cause of your problem. Once you have that, there is no way around having to scan the list, and inspect the type of each element to correct it, and your solution to that is as good as any (certainly, better, than the one, suggested in the other answer :)).

I would really rethink the code path that leads to having a heterogeneous list like this in the first place though if I were you. This is not really a good approach, because you subvert the type safety this way, and end up with a List[AnyRef], that can contain ... well, anything.

Sign up to request clarification or add additional context in comments.

4 Comments

Yes. You're right. I'm new to scala, sometimes language`s features lead to bad solutions just because they work :) But in this case I should start refactoring. Thank you.
Exactly my thoughts. I'm tempted to +1 but strictly speaking this is more a comment than an answer.
@maasg it's an answer, all right. The question was whether the was a better way to flatten than the OP had. The answer is "no". :)
after some deliberation +1 :-)
1

I don't think you can avoid having to deal with 2 cases: single element vs list. One way or another you would have to tell your program what to do. Here is a more general implementation that deals with a list of any depth:

def flattenList(xs: List[Any]): List[Any] =
  xs match {
    case Nil => Nil
    case (ys:List[_]) :: t => flattenList(ys) ::: flattenList(t)
    case h :: t => h :: flattenList(t)
  }

Example:

scala> flattenList(List("I", "can't", List("do", "this")))
res1: List[Any] = List(I, can't, do, this)

scala> flattenList(List("I", "can't", List("do", List("this", "and", "this"))))
res2: List[Any] = List(I, can't, do, this, and, this)

This does not look very type safe though. Try to use a Tree or something else.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.