12

I'm brand new to Scala, having had very limited experience with functional programming through Haskell.

I'd like to try composing a list of all possible pairs constructed from a single input list. Example:

val nums = List[Int](1, 2, 3, 4, 5)   // Create an input list
val pairs = composePairs(nums)        // Function I'd like to create

// pairs == List[Int, Int]((1, 1), (1, 2), (1, 3), (1, 4), (1, 5), (2, 1) ... etc)

I tried using zip on each element with the whole list, hoping that it would duplicate the one item across the whole. It didn't work (only matched the first possible pair). I'm not sure how to repeat an element (Haskell does it with cycle and take I believe), and I've had trouble following the documentation on Scala.

This leaves me thinking that there's probably a more concise, functional way to get the results I want. Does anybody have a good solution?

KChaloux
  • 3,918
  • 6
  • 37
  • 52
  • 3
    It's very helpful to learn the terminology for the operation you want to perform. In this case, you are trying to find the product of the list with itself. Look at this: http://stackoverflow.com/questions/8217764/cartesian-product-of-two-lists – Marcin Aug 03 '12 at 21:23
  • @Marcin Thank you. I've found one of the biggest stumbling blocks towards learning any degree of functional programming is picking up the new terminology. – KChaloux Aug 03 '12 at 21:41
  • Cross product? Same as Cartesian product? If Spark is relevant here , then [this answer](http://stackoverflow.com/a/26565173/1175496) is also relevant – Nate Anderson Jul 22 '16 at 07:44

3 Answers3

30

How about this:

val pairs = for(x <- nums; y <- nums) yield (x, y)
dbyrne
  • 59,111
  • 13
  • 86
  • 103
  • Works spectacularly. I'm not entirely sure *why* it works though. Is `for (x <- nums; y <- nums)` like a concise way of saying `for (x <- nums) for (y <- nums)`? – KChaloux Aug 03 '12 at 21:44
  • 7
    @KChaloux: It's syntactic sugar for `nums.flatMap(x => nums.map((x, _)))`, which is essentially `nums.map(x => nums.map((x, _))).flatten`, if that helps. – Travis Brown Aug 03 '12 at 21:53
  • @dbyrne It's definitely insight I could use. – KChaloux Aug 03 '12 at 22:09
11

For those of you who don't want duplicates:

val uniquePairs = for {
      (x, idxX) <- nums.zipWithIndex
      (y, idxY) <- nums.zipWithIndex
      if idxX < idxY
    } yield (x, y)

val nums = List(1,2,3,4,5)
uniquePairs: List[(Int, Int)] = List((1, 2), (1, 3), (1, 4), (1, 5), (2, 3), (2, 4), (2, 5), (3, 4), (3, 5), (4, 5))
cuz
  • 1,172
  • 11
  • 12
1

Here's another version using map and flatten

val pairs = nums.flatMap(x => nums.map(y => (x,y)))

List[(Int, Int)] = List((1,1), (1,2), (1,3), (1,4), (1,5), (2,1), (2,2), (2,3), (2,4), (2,5), (3,1), (3,2), (3,3), (3,4), (3,5), (4,1), (4,2), (4,3), (4,4), (4,5), (5,1), (5,2) (5,3), (5,4), (5,5))

This can then be easily wrapped into a composePairs function if you like:

def composePairs(nums: Seq[Int]) =
    nums.flatMap(x => nums.map(y => (x,y)))
Rok Kralj
  • 46,826
  • 10
  • 71
  • 80
Brian
  • 20,195
  • 6
  • 34
  • 55