2

In languages with support for unary addition, and in situations where a long list of operations is performed against sequential items in an array-like structure I might create a simple counter "int counter= 0;" and do the following:

someOperation(array[counter++]); nextOperation(array[counter++]); subsequentOperation(array[counter++]); .. etc 

What is an idiomatic way in scala to achieve similar behavior - i.e. avoid requiring hard-coded input array indices? Here is a specific example: a simple record parser method that converts a tab-separated call detail to a Call object. Not knowing any better way I did an ugly thing of putting in an AtomicInteger. But what is an acceptable way to do this?

Note: we can not simply do a collective operation here because some of the columns require ".toInt" processing and others do not.

 def parseStringToCall(text: String) = { val toks = text.split('\t') val ix = new AtomicInteger(0) new CallUpdate( toks(ix.getAndIncrement), // callDate toks(ix.getAndIncrement), // calledNumber toks(ix.getAndIncrement), // callingNumbe toks(ix.getAndIncrement), // cellTowersVi toks(ix.getAndIncrement), // direction toks(ix.getAndIncrement), // iMSI toks(ix.getAndIncrement), // manufacturer toks(ix.getAndIncrement), // phoneType toks(ix.getAndIncrement), // reasonforDro toks(ix.getAndIncrement).toInt, // weeknum toks(ix.getAndIncrement), // firstCellTow toks(ix.getAndIncrement), // lastCellTowe toks(ix.getAndIncrement).toInt, // calls toks(ix.getAndIncrement), // distinctCell toks(ix.getAndIncrement), // droppedCall toks(ix.getAndIncrement).toInt, // handovers toks(ix.getAndIncrement).toInt, // setupTime toks(ix.getAndIncrement).toInt // talkTime ) 
4
  • Related: stackoverflow.com/questions/14797547/… Commented May 27, 2014 at 17:53
  • Yes it is related. How did you find it? The title of the other one "Incrementing and getting value" is simplistic and does lend itself to being located by an obvious search Commented May 27, 2014 at 18:06
  • 2
    "scala increment int expression" was my google search query. Commented May 27, 2014 at 18:08
  • Thanks. I actually prefer my solution to the ones in that linked question. But i am upvoting your search comment anyways. Commented May 27, 2014 at 18:16

3 Answers 3

3

You're approaching the problem imperatively. Idiomatic Scala is more about functional programming. You have to get used to treating functions as values and exploit that power.

Your problem can be solved functionally like this:

toks // Get a lazy wrapper around `toks`, so that all the subsequent // operations will be done in just a single traversal: .view // Pair each item of `toks` up with an according operation: .zip(List(someOperation(_), nextOperation(_), subsequentOperation(_))) // Traverse thru those pairs, applying the operations: .foreach{ case (t, f) => f(t) } 

It must be noted that the operations are expected to be of type String => Unit.

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

8 Comments

+1 This is an insightful/helpful answer. I also liked the one from @ipiepiora. Will think a bit more before deciding which to award.
how is this helping? how would you convert an Array[String] in the parameter list that needs to be passed to callUpdate ? And how would you pass it?
@maasg The OPs question was quite vague, so I showed how he can do side-effects. If you need to collect the results of those functions and pass it to a vararg function, you can do something like callUpdate( toks.view.zip(..).map{case (t, f) => f(t)} : _* )
-1 this is plain wrong. Given val op = String=>Unit = s=>() (That's the type of 'someOperation(_)' as explained in the answer). What would be the type of: Array("one","two","three").view.zip(List(op,op,op)).foreach{case (t,f) => f(t) }. The answer is Unit. That's the result of a foreach(), and the operation will not modify in any way the original array. The only correct part of this answer is the first paragraph.
@NikitaVolkov I think I did and I still don't see how the answer helps the OP or solves the question at hand. The usecase in the question is clear: Based on an array of String, how can s/he pass elements of such array to a fairly large method call that takes specific types as input. Would be nice if you could show how that can be done based on your approach. It's not clear, at least to me.
|
3

Maybe you could not use index to access an array, but use pattern matching.

Given you have your call update defined as a case class (I've omitted some of the fields)

case class CallUpdate( callDate: String, calledNumber: String, callingNumber: String, cellTowersVi: String, direction: String, iMSI: String, manufacturer: String, phoneType: String, reasonforDro: String, weekNum: Int, firstCellTow: String, calls: Int ) 

You could write your parseStringToCall like this

def parseStringToCall(text: String) = text.split('\t') match { case Array ( callDate, calledNumber, callingNumber, cellTowersVi, direction, iMSI, manufacturer, phoneType, reasonforDro, weekNumString, firstCellTow, callsString ) => CallUpdate(callDate, calledNumber, callingNumber, cellTowersVi, direction, iMSI, manufacturer, phoneType, reasonforDro, weekNumString.toInt, firstCellTow, callsString.toInt) } 

Additionally using this approach, you could handle lines, which do not match, by adding wildcard case, and return None for example.

PS. Maybe you could think of splitting your CallUpdate into smaller, specialized objects.

1 Comment

+1 I think this approach is worth considering. I will hold off a bit on awarding answer, to see what other ideas come in.
1

For the sake of future generations searching for the question on the title, this is a potential implementation of postIncrement in Scala:

class Counter(start:Int = 0) { var current = start def ++ : Int = {val x=current; current +=1 ; x} } repl> val i = new Counter repl> i: Counter = Counter@2a01a9f2 repl> i++ repl> res26: Int = 0 repl> i++ repl> res27: Int = 1 

Now, to address the specific usecase on the body of the question: i.e. iterating over an array without having to deal with the index, probably the most straightforward way to do that is using an iterator:

val args = Array("arguments","with","ints","23","46") val iter = args.iterator val res = CallUpdate(iter.next, iter.next, iter.next, iter.next.toInt, iter.next.toInt) 

This solves the index access in a clean way.

Can we do better? Probably. We could address the type conversion in a more elegant way. For that we define a simple marker type and implicit conversions to avoid having to take care of those toInt calls. This can of course be extended to other types like Date, Long, ...

case class Param(value:String) object Param { implicit def paramToString(p:Param):String = p.value implicit def paramToInt(p:Param):Int = p.value.toInt } 

and we can do something like:

val args = Array("arguments","with","ints","23","46") val params = args.map(elem => Param(elem)) val iter = params.iterator val res = CallUpdate(iter.next, iter.next, iter.next, iter.next, iter.next) 

And if you want a mind-blowing solution, I recommend you this reading: applying-an-argument-list-to-curried-function

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.