Reputation: 4077
I have a string with results of the athlete in the competition in the high jump. Here, + corresponds to a successful attempt,% unsuccessful, - skipped. The height and the corresponding attempts are separated by a space. I need to read the string and return the maximum height taken, or -1 if there is none or the format of the string is broken. For example:
"220 + 224 %+ 228 %- 230 + 232 %%- 234 %".// result in 230
"226 +" // result is 226
"???" // result is -1
I tried to use regular expressions, but did not achieve much success.
fun bestHighJump(jumps: String): Int {
var result = 0
val jumpsSplitted = jumps.split(" ")
val regStr = Regex("""[.+\d]""")
for (item in jumpsSplitted) {
if (regStr.containsMatchIn(item)) result += item
}
//Don't know what to do next
}
Help with the solution of the problem and if not difficult, recommend resources where I can well learn the topic of string formatting.
Upvotes: 0
Views: 813
Reputation: 29844
Using regex is a good idea. Groups become especially handy here.
([\\d]{1,3})([ +\\-%]*)
The regex will match any 1 to 3 digit number in its first group and the result of that attempt in the second group.
You retrieve a Sequence<MatchResult>
with findAll
and then you filter and map the values of interest.
In the end you return the maximum of the successful attempts or -1 if the input string was invalid or did not contain a successful jump.
fun bestHighJump(jumps: String): Int {
val attempts = Regex("([\\d]{1,3})([ +\\-%]*)").findAll(jumps)
val list = attempts.filter {
it.groups[2]?.value?.let {
'+' in it
} == true
}.map {
it.groups[1]?.value
}.mapNotNull { it?.toInt() }.toList()
return if (list.isEmpty()) {
-1
} else {
list.max()!!
}
}
Note that in Kotlin's MatchGroupCollection
the first group will have the index 1 and the second the index 2 because the group with index 0 are both groups combined.
Upvotes: 1
Reputation: 164064
First you need to trim and clean the argument jumps
from multiple adjacent spaces.
Then after splitting it, create 2 lists:
the 1st contains the heights and
the 2nd contains the attempts.
It is necessary to check that the 1st list contains valid integers
and the 2 lists are the same size.
Then by removing all occurrences of %
and -
in every item of the 2nd list,
you get the index of the last +
item.
This index is used to get the corresponding height from the 1st list.
fun bestHighJump(jumps: String): Int {
val jumpsSplitted = jumps.trim().replace(Regex("\\s\\s+"), " ").split(" ")
if (jumpsSplitted.size < 2 || jumpsSplitted.size % 2 == 1)
return -1
val heights = jumpsSplitted.filterIndexed { i, _ -> (i % 2 == 0)}.mapNotNull { it.toIntOrNull() }
val attempts = jumpsSplitted.filterIndexed { i, _ -> (i % 2 == 1)}
if (heights.size != attempts.size)
return -1
val index = attempts
.map { it.replace("%", "").replace("-", "") }
.indexOfLast { it == "+" }
return if (index == -1) -1 else heights[index]
}
This
val jumps1 = "220 + 224 %+ 228 %- 230 + 232 %%- 234 %"
println(bestHighJump(jumps1))
will print 230
This
val jumps2 = "226 +"
println(bestHighJump(jumps2))
will print 226
This
val jumps3 = "???"
println(bestHighJump(jumps3))
will print -1
Upvotes: 2
Reputation: 18537
(Rewritten in view of the question being clarified.)
As I understand it now, the string should contain integers alternating with symbols, and we want the largest integer which is followed by "+".
I'd approach this by splitting the string into words, and then looking at pairs of words. We can do this with the zipWithNext()
function, which gives a list of all the adjacent pairs. We can then filter()
to select only those where the second word is "+", mapNotNull()
to convert the first word of such a pair to an Int
where possible (ignoring the null
s from those which aren't valid integers), and take the max()
of those numbers. (If there aren't any, max()
returns null
, so we can use the Elvis operator to substitute -1 instead.)
fun String.bestHighJump()
= split(" ")
.zipWithNext()
.filter{ it.second == "+" }
.mapNotNull{ it.first.toIntOrNull() }
.max() ?: -1
(I've made this an extension function on String
, mostly because it seems to fit well; it also avoids having to declare and then use a parameter. But a normal function would work in much the same way.)
To make this more idiomatic, it would probably be better to remove the Elvis operator and return a null
directly if there were no matching scores; that makes the situation more obvious to the caller, who can then decide how to handle that case.
Since you're looking at regexes, here's an alternative solution using one of those:
fun String.bestHighJump()
= Regex("""([0-9]+) [+]\B""")
.findAll(this)
.map{ it.groupValues[1].toInt() }
.max() ?: -1
This is one line shorter, but I think it's a lot less clear. In my experience regexes are often hard to get right, to debug, and to maintain, so I prefer other approaches. (For comparison, the first version worked first time, while the regex version took many attempts!)
Here the regex matches one successful result (one or more digits, followed by a space, a +
, and then a word boundary. (The latter is needed because we don't know whether this result is the last one in the string, or is followed by others. Regexes match greedily, so we don't need one at the start as well.)
findAll()
searches the whole string, and returns a sequence of matches; we then take the first group from each (which is the number), convert it to an int (no need to handle invalid numbers this time, if the regex works), and take the max as before.
(The question still isn't clear on how to handle success indicators with multiple characters. I'm assuming we only want to count results where the success indicator is just the one character '+'. If we should also include cases where there is a '+' among '%'s and/or other characters, then both functions can be tweaked for that. But again, the regex one would be harder to do.)
As for where to learn this sort of thing, I'm assuming you already know about the Kotlin docs. (And I'd recommend the Kotlin In Action book for learning the language.) There are different approaches to string processing, depending on your needs, so it's harder to recommend — but the basic principles aren't specific to Kotlin, so there are probably many places to look. And if you have a trickier case, you could always post it as another question here!
Upvotes: 2