I'm new to Scala, and I'm getting confused on how functions stitch together and their return types. Consider the following:
val nodes = List(0,1,2)
val links = List(List(1, 2), List(1, 0), List(1,3))
Each node has a bi-directional link, as described in the tuple-pair 'links'. I am attempting to building a map where each node points to it's neighbours, for example
Map(0 -> List(1), 1 -> List(2, 0), 2 -> List(1) )
However, my initial thinking on how to code this has me stumped on why it's returning an empty 'List[Any]' value.
nodes.foreach(z => (links.map { case List(a,b) => if(a == z) a else if (b == z) b }))
What is the right way to do this?
Or,
scala> nodes.map(n => (n, links.flatMap {
| case List(`n`, x) => Some(x)
| case List(x, `n`) => Some(x)
| case _ => None }))
res3: List[(Int, List[Int])] = List((0,List(1)), (1,List(2, 0, 3)), (2,List(1)))
As commented,
scala> .toMap
res4: scala.collection.immutable.Map[Int,List[Int]] = Map(0 -> List(1), 1 -> List(2, 0, 3), 2 -> List(1))
There are some problems in your code:
First of all, foreach
returns Unit
as you can see in the Scala Doc
Also, your condition is not entirely correct - eg for node 0
and connection 1<->0
you are going to print 0
but you actually want to return 1
.
The correct code would go like this:
val nodes = List(0,1,2)
val links = List(List(1, 2), List(1, 0), List(1,3))
val result = nodes.map(z =>
(z, links.flatMap {
case List(a,b) => if(a == z && nodes.contains(b)) Some(b) else if (b == z && nodes.contains(a)) Some(a) else None
}
)
).toMap
println(result)
Notice how I am calling map
on the nodes
list (instead of foreach
) - this gives me then the possibility to call toMap
at the end - which is returning a Map (what you want to achieve eventually). Also I am map ping each node to a Pair
of the node and a List of its neighbours
Note 2: I added a condition which checks if the discovered neighbour is part of the nodes collections (with contains
) - as this is how your example is. If you have a different requirement, just remove it
Will give:
Map(0 -> List(1), 1 -> List(2, 0), 2 -> List(1))
I am not exactly sure where you get a List[Any]
in your code example.
However, foreach
takes a function with return type Unit
as argument and therefore returns Unit
. It is basically a classic for each loop for side effects.
The 'map' function of the List
collection can turn a List[A]
into a List[B]
. So if your were for example to return either a List[Int]
or an Int
in your map
function, the smallest common ancestor type of both would Any
so the result would be a List[Any]
.
However, as scala has a very powerful collections API, there is an easy solution.
Explicitly, you can provide a CanBuildFrom[List[Int], List[Int], Map[Int, Int]]
to build a Map[Int, List[Int]]
from a List[Int]
while mapping using a function Int => (Int, Int)
But scala provides an even easier way to accomplish that (Have a look at breakOut ).
So, if I interpreted your question correctly, you would need something like this:
val neighbors: Map[Int, List[Int]] = nodes.map(z =>
z -> links.withFilter(_.contains(z)).flatMap(_.filter(candidate => candidate != z && nodes.contains(candidate)))
)(collection.breakOut)
--> neighbors: Map[Int,List[Int]] = Map(0 -> List(1), 1 -> List(2, 0), 2 -> List(1))
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.