0

Hey i am pretty new to functional programming so what is the most scala-like way to write this peace of code?

var ANSWERS_ARRAY // Is array
for ((rows, i) <- SOME_2D_ARRAY.view.zipWithIndex)
      for ((col, j) <- rows.view.zipWithIndex)
        if (col == SOME_VALUE)
          // add tuple of (SOME_VALUE, (i,j)) to an ANSWERS_ARRAY

Here i am looping through a 2D array and checking if a specified element matches the condition. If yes, i put it to a new array. Is there one or two liner for that? Because this code is ugly and inefficient.

2 Answers 2

2

The logic of your code is not very functional to begin with.

In functional programming, you usually deal with immutable data, on which you apply functions to get new immutable values. Here, you're trying to modify the value of answer_array imperatively.

The idiomatic way to do it would be the following:

val answerArray = my2DArray.zipWithIndex.flatMap {
  case (row, i) => row.zipWithIndex.filter{
    case (col, j) => col == someValue
  }.map {
    case (col, j) => (someValue, (i, j))
  }
}

This is not very readable code, especially for a beginner, so let's decompose it:

  • first we traverse our 2D array (with indices) using flatMap, which takes a function to act on each element. This function must return another array for each element, and will flatten all these arrays into a single one (hence the "flat" in flatMap).

  • the elements of the array are pairs of an element of the initial array, ie a row, and its index, so we pattern match on this pair, to be able to define our function properly.

  • now, for each row, what do we want to do? keep only the elements which satisfy the condition, so we filter on it, with the predicate (which is just a fancy name for function returning a Boolean), returning the array containing only the values satisfying the condition.

  • however, we do not want these values, but a transformation of them, so we apply map to our array. map is the same as flatMap, only its argument does not need to return arrays, since the return value will not be flattened (it just makes an array of the images of the element of the first array by the given function).

  • what function do we want to apply on the remaining elements? Well, we just want the triple (someValue, (i, j))

  • notice that I used camelCase, which is also the idiomatic way to name your variables in scala (although this is in no way necessary)

Ok, so we have some nice code that do what we want, but it is not very readable. Thankfully, the language as a nice syntax sugar called for-comprehension to replace flatMaps, maps and filters into a more imperative style. Note that this is just for easiness of code-writing, and that, at compile-time, the code will be replaced by what I described above (or something close to it).

val answerArray = for {
  (row, i) <- 2DArray.zipWithIndex
  (col, j) <- row.zipWithIndex
  if col == someValue
} yield (someValue, (i, j))
  • Each <- line traverses the rhs array, assigning the lhs name to its elements (with some pattern matching as a bonus)

  • Each if row represent a condition on the values we want to keep

  • the yield part is the value we want to get in each element of our final array, depending on the values we extracted in each array.

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

2 Comments

Sir. Thank you for such a brief answer.
Sorry about that. I'd rather have a long explanation, than leave you in the jungle of functional programming without understanding what you're doing.
2

An easy solution, using zipWithIndex and a for-comprehension:

 val SOME_2D_ARRAY: List[List[Int]] = ???
 val SOME_VALUE: Int = ???
 val ANSWERS_ARRAY: List[(Int, (Int, Int))] = for {
   (rows, i) <- SOME_2D_ARRAY.zipWithIndex
   (col, j) <- rows.zipWithIndex
   if col == SOME_VALUE
 } yield (SOME_VALUE, (i,j))

If you want to implement lazy search (as in your snippet) then you can implement it like. Note that, in this version, ANSWERS_ARRAY is a lazy collection.

 val ANSWERS_ARRAY = for {
   (rows, i) <- SOME_2D_ARRAY.view.zipWithIndex
   (col, j) <- rows.view.zipWithIndex
   if col == SOME_VALUE
 } yield (SOME_VALUE, (i,j))

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.