Removing unncecessary parts from String - kotlin

I'm consuming a client that I cannot change and it sends me data that looks like that:
"BOOKING - PAID (price.amount=70, price.currency=EUR)"
and I would like to retrieve from that only 70 EUR
What is the best way to do such thing in kotlin? I didn't find any removeAll("", "", ...) functions for String, only replace but would have to chain them to remove both price.amount and price.currency.
EDIT:
Need to get BOOKING - PAID (70 EUR) actually, forgot about that part.

Thinking about it and as you updated your question to really remove only a part from the string, here are some approaches for removing several strings from a given string:
Using regex:
input.replace("""(price\.(amount|currency)=|,)""".toRegex(), "")
Using a list of strings to remove:
sequenceOf(input, "price.amount=", ",", "price.currency=")
.reduce { acc, rm -> acc.replace(rm, "") }
// alternatively using var:
var input = TODO()
sequenceOf("price.amount=", ",", "price.currency=")
.forEach { input = input.replace(it, "") }
Still: most of the time I would rather take the other route: extracting the information you require and just print that, as also Baptiste has shown in his answer. Otherwise you may start to expose answers of that service you didn't want to expose in the first place.

This sounds like a job for regular expressions!
fun main(args: Array<String>) {
val str = "BOOKING - PAID (price.amount=70, price.currency=EUR)"
// The expressions between parentheses will map to groups[1], groups[2] and groups[3] respectively
val reg = Regex("""(.*) \(price\.amount=([0-9]+), price\.currency=([A-Z]+)\)""")
// Apply the regular expression on the string
val results = reg.find(str)
results?.groupValues?.let { groups ->
// If results and groupValues aren't null, we've got our values!
val type = groups[1]
val price = groups[2]
val currency = groups[3]
println("$type ($price $currency)") // BOOKING - PAID (70 EUR)
}
// Or as suggested by #Roland:
results?.destructured?.let { (type, price, currency) ->
println("$type ($price $currency)") // BOOKING - PAID (70 EUR)
}
}
Regular expressions allow you to take a string as entry, and find a pattern in it. They're quite used in all languages, you can find more info about them all over the place.
EDIT: edited for the updated question. I chose to treat "BOOKING - PAID" as a single string, but there's an infinite number of ways to do it, depending on your granularity needs; and honestly, at that point a regex might be a bit overkill. :)

Without regex by string manipulation and assuming that this is the pattern:
fun main(args: Array <String> ) {
val str = "BOOKING - PAID (price.amount=70, price.currency=EUR)"
val type = str.substringBefore("(").trim()
val price = str.substringBeforeLast(",").substringAfter("=")
val currency = str.substringAfterLast("=").substringBefore(")")
val result = "$type ($price $currency)"
println(result)
}
will print
BOOKING - PAID (70 EUR)
Edit: I use str.substringBeforeLast(",") to get the price, in case , could be used as a delimeter for decimal part in the number

Related

Convert String into list of Pairs: Kotlin

Is there an easier approach to convert an Intellij IDEA environment variable into a list of Tuples?
My environment variable for Intellij is
GROCERY_LIST=[("egg", "dairy"),("chicken", "meat"),("apple", "fruit")]
The environment variable gets accessed into Kotlin file as String.
val g_list = System.getenv("GROCERY_LIST")
Ideally I'd like to iterate over g_list, first element being ("egg", "dairy") and so on.
And then ("egg", "dairy") is a tuple/pair
I have tried to split g_list by comma that's NOT inside quotes i.e
val splitted_list = g_list.split(",(?=(?:[^\\\"]*\\\"[^\\\"]*\\\")*[^\\\"]*\$)".toRegex()).toTypedArray()
this gives me first element as [("egg", second element as "dairy")] and so on.
Also created a data class and tried to map the string into data class using jacksonObjectMapper following this link:
val mapper = jacksonObjectMapper()
val g_list = System.getenv("GROCERY_LIST")
val myList: List<Shopping> = mapper.readValue(g_list)
data class Shopping(val a: String, val b: String)
You can create a regular expression to match all strings in your environmental variable.
Regex::findAll()
Then loop through the strings while creating a list of Shopping objects.
// Raw data set.
val groceryList: String = "[(\"egg\", \"dairy\"),(\"chicken\", \"meat\"),(\"apple\", \"fruit\")]"
// Build regular expression.
val regex = Regex("\"([\\s\\S]+?)\"")
val matchResult = regex.findAll(groceryList)
val iterator = matchResult.iterator()
// Create a List of `Shopping` objects.
var first: String = "";
var second: String = "";
val shoppingList = mutableListOf<Shopping>()
var i = 0;
while (iterator.hasNext()) {
val value = iterator.next().value;
if (i % 2 == 0) {
first = value;
} else {
second = value;
shoppingList.add(Shopping(first, second))
first = ""
second = ""
}
i++
}
// Print Shopping List.
for (s in shoppingList) {
println(s)
}
// Output.
/*
Shopping(a="egg", b="dairy")
Shopping(a="chicken", b="meat")
Shopping(a="apple", b="fruit")
*/
data class Shopping(val a: String, val b: String)
Never a good idea to use regex to match parenthesis.
I would suggest a step-by-step approach:
You could first match the name and the value by
(\w+)=(.*)
There you get the name in group 1 and the value in group 2 without caring about any subsequent = characters that might appear in the value.
If you then want to split the value, I would get rid of start and end parenthesis first by matching by
(?<=\[\().*(?=\)\])
(or simply cut off the first and last two characters of the string, if it is always given it starts with [( and ends in )])
Then get the single list entries from splitting by
\),\(
(take care that the split operation also takes a regex, so you have to escape it)
And for each list entry you could split that simply by
,\s*
or, if you want the quote character to be removed, use a match with
\"(.*)\",\s*\"(.*)\"
where group 1 contains the key (left of equals sign) and group 2 the value (right of equals sign)

How to iterate over a Triple in Kotlin

I have list of three set of values which are related to each other. i.e. Roll Number, Student Name and School Name.
I am using Kotlin Triple to store them.
Below is the code:
val studentData = listOf(
Triple(first = "1", second ="Sam", third = "MIT"),
Triple(first = "2", second ="Johnny", third = "SYM"),
Triple(first = "3", second ="Depp", third = "PIT")
)
And now I need to build a function which will accept roll number and will return either student name or school name. Something like below:
fun getStudentDetails(rollNumber: String) : String {
//...
//return student name or school name
}
How to achieve this?
How to traverse the Triple in most preformat way considering below:
a) the time and space complexity
b) the list of student details can grow large
Given that rollNumber is a String you can just filter the list using firstOrNull and return the student name or school name:
fun getStudentDetails(rollNumber: String) : String =
studentData.firstOrNull({ (roll, _, _) ->
roll == rollNumber
})?.second ?: "No student with $rollNumber"
The space complexity would be constant, the time complexity is O(n) at the worst case.

Combining Two List in Kotlin with Index

There is a data class as fruits.
data class Fruits(
val code: String, //Unique
val name: String
)
The base list indexed items with boolean variable is as below.
val indexList: MutableList<Boolean> = MutableList(baseFruitList.size) { false }
Now the Favourite Indexed list is as below
val favList: MutableList<Boolean> = MutableList(favFruitList.size) { true}
I want a combined full list which basically has the fav item indicated as true.
Ex:
baseFruitList = {[FT1,apple],[FT2,grapes],[FT3,banana],[FT4,mango],[FT5,pears]}
favList = {[FT2,grapes],[FT4,mango]}
The final index list should have
finalIndexed = {false,true,false,true,false}
How can we achieve in Kotlin, without iterating through each element.
You can do
val finalIndexed = baseFruitList.map { it in favList }
assuming, like #Tenfour04 is asking, that name is guaranteed to be a specific value (including matching case) for a specific code (since that combination is how a data class matches another, e.g. for checking if it's in another list)
If you can't guarantee that, this is safer:
val finalIndexed = baseFruitList.map { fruit ->
favList.any { fav.code == fruit.code }
}
but here you have to iterate over all the favs (at least until you find a match) looking to see if one has the code.
But really, if code is the unique identifier here, why not just store those in your favList?
favList = listOf("FT2", "FT4") // or a Set would be more efficient, and more correct!
val finalIndexed = baseFruitList.map { it.code in favList }
I don't know what you mean about "without iterating through each element" - if you mean without an explicit indexed for loop, then you can use these simple functions like I have here. But there's always some amount of iteration involved. Sets are always an option to help you minimise that

how to search for series of strings in a sentence in kotlin

I have an ArrayList of items. Each item has long strings for example
("The cat is in the hat","It's warm outside","It's cold outside")
what I am trying to do is search for a series of strings for example "It's outside" in any given order in the ArrayList above and it should find 2 of them.
This is what I tried:
fun clickItem(criteria: String) {
productList = productListAll.filter {it: Data
it.title.contains(criteria, ignoreCase = true)
}
} as ArrayList<Data>
This works fine when the words I am looking for are in sequence. However, I am trying to get strings in any given order. Does anyone know how to accomplish that?
We can do this by splitting title and criteria by whitespaces to create a set of words. Then we use containsAll() to check if title contains all of words from criteria. Additionally, we need to convert both of them to lowercase (or uppercase), so the search will be case-insensitive:
private val whitespace = Regex("\\s+")
fun clickItem(criteria: String): List<Data> {
val criteriaWords = criteria.lowercase().split(whitespace).toSet()
return productListAll.filter {
it.title.lowercase().split(whitespace).containsAll(criteriaWords)
}
}
Note that searching through text is not that trivial, so simple solutions will be always limited. For example, we won't find "it's" when searching for "it is", etc.

Specific regex in Kotlin

I created two functions .One checkUnit to get the unit from string and second whatU if input contains m u n p T g k to convert the input value .But there is some mismatch .
My pattern examples:
"m(O|h|F|s|H|A|V)" -this is for the m before unit this part needs improve
\b0-9.Ohm.(?<![0-9])\b" - this is for Ohm this part is wrong
val pattern = Regex(whatToFind)
val result = pattern.containsMatchIn(whatToFind)
This is for all invalid characters in input [A-EI-LNP-SUW-Za-gi-jloq-tw-zvV/ '$&+,{}:;=_\[]|`~?##"<>^*()%!-£€¥¢©®™¿÷¦¬×§¶°]
How to check if m is before Ohm and after number in string 100mOhm in regex Kotlin in more effective way ?
You can take a look at the Pattern documentation. There are several predefined character classes like \d+ for a digit. So you can use the following method:
boolean matched = Pattern.matches("\\d+mOhm", "100mOhm");
Or if you want to have the pattern for a longer time you can use the following method:
Pattern pattern = Pattern.compile("\\d+mOhm");
Matcher matcher = pattern.matcher("100mOhm");
boolean matched = matcher.matches();
If you want to use only Kotlin, you can use the following code:
val regex = "\\d+mOhm".toRegex()
val matched = regex.matches("100mOhm")
I found also a nice tutorial with more information about Koltin Regex.
This meets your requirement:
import java.util.Scanner
fun main() {
val scanner = Scanner(System.`in`)
val s = scanner.nextLine()
println("\\d+mOhm".toRegex().matches(s))
}
Perhaps you can modify it from here.