2

I need to calculate the number of integers and floats i have in a Map which is like Map[String, List[(Int, String, Float)]]

The data comes from reading a file - the data inside for example looks kinda like (however there is a few more Routes):

Cycle Route (City),1:City Centre :0.75f,2:Main Park :3.8f,3:Central Station:2.7f,4:Modern Art Museum,5:Garden Centre:2.4f,6:Music Centre:3.4f

The map is split so that the String is the name of the route and the List is the rest of the data.

I want it to calculate the number of 'checkpoints' per route and total distance of each route (which is the float) then print out e.g. Oor Wullie Route has 6 checkpoints and total distance of 18.45km

I am guessing I need to use a foldLeft however i am unsure how to do so?

Example of a simple fold i have done before but not sure how to apply one to above scenario?

val list1 = List.range(1,20)

def sum(ls:List[Int]):Int = {
  ls.foldLeft(0) { _ + _}
}
CycleNew
  • 31
  • 4

1 Answers1

3

You could do this with a fold, but IMO it is unnecessary.

You know the number of checkpoints by simply taking the size of the list (assuming each entry in the list is a checkpoint).

To compute the total distance, you could do:

def getDistance(list: List[(Int, String, Float)]): Float = 
  list
    .iterator // mapping the iterator to avoid building an intermediate List instance
    .map(_._3) // get the distance Float from the tuple
    .sum // built-in method for collections of Numeric elements (e.g. Float)

And then get your printout like:

def summarize(routes: Map[String, List[(Int, String, Float)]]): Unit =
  for { (name, stops) <- routes } {
    val numStops = stops.size
    val distance = getDistance(stops)
    println(s"$name has $numStops stops and total distance of $distance km")
  }

If you really wanted to compute both numStops and distance via foldLeft, Luis's comment on your question is the way to do it.

edit - per Luis's request, putting his comment in here and cleaning it up a bit:

stops.foldLeft(0 -> 0.0f) { 
   // note: "acc" is short for "accumulated"
   case ((accCount, accDistance), (_, _, distance)) => 
     (accCount + 1) -> (accDistance + distance) 
}
Dylan
  • 13,645
  • 3
  • 40
  • 67
  • Note that `size` on List is **O(n)** this traverse the list twice. BTW, I would like it if you can include my comment in your answer _(I mean, put the code well indented)_ so I can delete it, especially since I had a typo and the float was the third position and not the second. – Luis Miguel Mejía Suárez Apr 15 '20 at 16:03
  • 1
    @LuisMiguelMejíaSuárez ok I've added it. I agree that using `size` may be a tiny bit slower, but I think the difference will be negligible, especially since the lists are unlikely to be very long given OP's use case. It's the difference between "iterate one, doing two things per step" and "iterate twice, doing one thing per step". – Dylan Apr 15 '20 at 16:25
  • @LuisMiguelMejíaSuárez Thanks both for your solutions - I am trying to implement this into a menu so when the user selects menu option for Total Distance and Stages they will get the calculated output. When I have implemented your solution I am getting the following error back: Error:(135, 19) type mismatch; found : (Int, Float) required: Float stops.foldLeft(0 -> 0.0f) { Do you know how I could fix this? – CycleNew Apr 15 '20 at 16:43
  • Are you doing the `foldLeft` inside the `getDistance` function I gave in my answer? That is marked as having a `Float` return type that is just the distance value, whereas the `foldLeft` is returning an `(Int, Float)` which is both the count and the distance. – Dylan Apr 15 '20 at 17:09
  • @Dylan that's exactly what I was doing, I have now changed it to (Int, Float) and the error is gone Thanks!! I am having trouble with the print statement - I tried implementing it in a similar way you had given your example but it doesn't seem to like it? Sorry for all the questions its clear I am very new to this! – CycleNew Apr 15 '20 at 18:42
  • @CycleNew just speculating here; if I'm wrong you should probably open a new question. With `println` you pass a String, and you use the `s` prefix before the opening quote to make it use string interpolation. To put a value into your string, you do `$nameOfValue`, or to put an expression (e.g. if it has spaces or punctuation), wrap it with curly braces e.g. `${thing._1}`. Maybe you forgot the `s` or maybe you need the curly braces. – Dylan Apr 18 '20 at 17:14