Kotlin algorithm to reverse a given string - kotlin

I'm creating an algorithm in Kotlin that should reverse a given string and output it, so, for example, the string would be "Hello World! and the output would be "olleH !dlroW. I know there's a function that does this already but I'm practising with loops and if statements so I'm doing it myself.
So far, I've got most of a working solution, the only problem with the code I have in that it only works with odd length strings, because of a while loop. I've put what I have in this post.
I'm stuck on what to change to make this program work with strings that are of an even length, currently with those strings, the program falls into an infinite loop because the condition is never met.
The exclusion part of the program will jump over characters that are included in the exclusion string, I've already tested this and it works fine, provided the string minus the skipped character is not of even length.
fun main() {
val userInput = ""
val exclusion = ""
val wordsInString = userInput.split(" ")
var wordsSize = wordsInString.size
var wordPointer = 0
while (wordPointer < wordsSize) {
var currentWord = wordsInString[wordPointer]
var charArray = currentWord.toCharArray()
var charPointerOne = 0
var charPointerTwo = currentWord.length - 1
while (charPointerOne != charPointerTwo) {
if (exclusion.contains(charArray[charPointerOne])) {
charPointerOne++
} else if (exclusion.contains(charArray[charPointerTwo])) {
charPointerTwo--
} else {
var charToSwtichOne = charArray[charPointerOne]
var charToSwitchTwo = charArray[charPointerTwo]
charArray[charPointerOne] = charToSwitchTwo
charArray[charPointerTwo] = charToSwtichOne
charPointerOne++
charPointerTwo--
}
}
wordPointer++
var outputString = String(charArray)
print(outputString + " ")
}
}

You need to change the loop control condition to use < instead of !=. because in case of even length strings they simply never meet and jump over. for example if you had a String of length two, then on first iteration charPointerOne will have value 0 and charPointerTwo will have value 1, and on the next iteration charPointerOne will be incremented to 1 and charPointerTwo will be decremented to 0 and these values still satisfy the loop control hence the loop continues. So to fix this change your code as
while (charPointerOne < charPointerTwo)

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)

Read one or two variables alternately in one line

I have declared 2 variables to read from console but on other case i want to read just one of them but i can't.
My code:
print("Enter two numbers in format: {source base} {target base} (To quit type /exit) ")
val (sourceBase, targetBase) = readLine()!!.split(" ")
`I can't type /exit because i've got IndexOutOfBoundsException.
Any tips?
Edit: Thank you all for respond, especially lukas.j, it's working now.
Add a second element, an empty string, if the splitted readLine() contains less than 2 elements:
val (sourceBase, targetBase) = readLine()!!.split(" ").let { if (it.size < 2) it + "" else it }

Increment String Value Google Sheet

Using the following code I cannot increment a value in google sheets to be plus one.
function incrementCellValuesByOne() {
// Increments the values in all the cells in the active range (i.e., selected cells).
// Numbers increase by one, text strings get a "1" appended.
// Cells that contain a formula are ignored.
var ss = SpreadsheetApp.getActiveSpreadsheet();
var activeRange = ss.getActiveRange();
var cell, cellValue, cellFormula;
// iterate through all cells in the active range
for (var cellRow = 1; cellRow <= activeRange.getHeight(); cellRow++) {
for (var cellColumn = 1; cellColumn <= activeRange.getWidth(); cellColumn++) {
cell = activeRange.getCell(cellRow, cellColumn);
cellFormula = cell.getFormula();
// if not a formula, increment numbers by one, or add "1" to text strings
// if the leftmost character is "=", it contains a formula and is ignored
// otherwise, the cell contains a constant and is safe to increment
// does not work correctly with cells that start with '=
if (cellFormula[0] != "=") {
cellValue = cell.getValue();
cellValue =+cellValue
cell.setValue(cellValue + 1);
}
}
}
}
For example "personalDataDOB_3" needs to become "personalDaTaDOB_4" I'm looking for a fast way to do this as right now I need to replace the value by typing.
You want to modify "personalDataDOB_3" of a certain cell to "personalDaTaDOB_4". If my understanding is correct, how about this modification?
Modification points :
When the retrieved "personalDataDOB_3" is converted to the number using cellValue =+cellValue, NaN is returned. So even if 1 was added, the result is NaN.
If the format of strings you want to modify is always "personalDataDOB_#", how about separating the string by _?
In order to reflect above points, please modify your script as follows.
From :
if (cellFormula[0] != "=") {
cellValue = cell.getValue();
cellValue =+cellValue
cell.setValue(cellValue + 1);
}
To :
if (cellFormula[0] != "=") {
cellValue = cell.getValue();
var temp = cellValue.split("_"); // Added
temp[1] = Number(temp[1]) + 1; // Added
cell.setValue(temp.join("_")); // Modified
}
Note :
If the format of strings you want to modify is always changed, please tell me.
If I misunderstand your question, I'm sorry.

Kotlin: how to swap character in String

I would like to swap a string from "abcde" to "bcdea". So I wrote my code as below in Kotlin
var prevResult = "abcde"
var tmp = prevResult[0]
for (i in 0..prevResult.length - 2) {
prevResult[i] = prevResult[i+1] // Error on preveResult[i]
}
prevResult[prevResult.length-1] = tmp // Error on preveResult[prevResult.lengt-1]
It errors out as stated above comment line. What did I do wrong? How could I fix this and get what I want?
Strings in Kotlin just like in Java are immutable, so there is no string.set(index, value) (which is what string[index] = value is equivalent to).
To build a string from pieces you could use a StringBuilder, construct a CharSequence and use joinToString, operate on a plain array (char[]) or do result = result + nextCharacter (creates a new String each time -- this is the most expensive way).
Here's how you could do this with StringBuilder:
var prevResult = "abcde"
var tmp = prevResult[0]
var builder = StringBuilder()
for (i in 0..prevResult.length - 2) {
builder.append(prevResult[i+1])
}
builder.append(tmp) // Don't really need tmp, use prevResult[0] instead.
var result = builder.toString()
However, a much simpler way to achieve your goal ("bcdea" from "abcde") is to just "move" one character:
var result = prevResult.substring(1) + prevResult[0]
or using the Sequence methods:
var result = prevResult.drop(1) + prevResult.take(1)
You can use drop(1) and first() (or take(1)) to do it in one line:
val str = "abcde"
val r1 = str.drop(1) + str.first()
val r2 = str.drop(1) + str.take(1)
As to your code, Kotlin String is immutable and you cannot modify its characters. To achieve what you want, you can convert a String to CharArray, modify it and then make a new String of it:
val r1 = str.toCharArray().let {
for (i in 0..it.lastIndex - 1)
it[i] = it[i+1]
it[it.lastIndex] = str[0] // str is unchanged
String(it)
}
(let is used for conciseness to avoid creating more variables)
Also, you can write a more general version of this operation as an extension function for String:
fun String.rotate(n: Int) = drop(n % length) + take(n % length)
Usage:
val str = "abcde"
val r1 = str.rotate(1)
Simpler solution: Just use toMutableList() to create a MutableList of Char and then join it all together with joinToString.
Example:
Given a String input, we want to exchange characters at positions posA and posB:
val chars = input.toMutableList()
val temp = chars[posA]
chars[posA] = chars[posB]
chars[posB] = temp
return chars.joinToString(separator = "")
Since Strings are immutable, you will have to copy the source string into an array, make changes to the array, then create a new string from the modified array. Look into:
getChars() to copy the string chars into an array.
Perform your algorithm on that array, making changes to it as needed.
Convert the modified array back into a String with String(char[]).

Lucene: how to preserve whitespaces etc when tokenizing stream?

I am trying to perform a "translation" of sorts of a stream of text. More specifically, I need to tokenize the input stream, look up every term in a specialized dictionary and output the corresponding "translation" of the token. However, i also want to preserve all the original whitespaces, stopwords etc from the input so that the output is formatted in the same way as the input instead of ended up being a stream of translations. So if my input is
Term1: Term2 Stopword! Term3
Term4
then I want the output to look like
Term1': Term2' Stopword! Term3'
Term4'
(where Termi' is translation of Termi) instead of simply
Term1' Term2' Term3' Term4'
Currently I am doing the following:
PatternAnalyzer pa = new PatternAnalyzer(Version.LUCENE_31,
PatternAnalyzer.WHITESPACE_PATTERN,
false,
WordlistLoader.getWordSet(new File(stopWordFilePath)));
TokenStream ts = pa.tokenStream(null, in);
CharTermAttribute charTermAttribute = ts.getAttribute(CharTermAttribute.class);
while (ts.incrementToken()) { // loop over tokens
String termIn = charTermAttribute.toString();
...
}
but this, of course, loses all the whitespaces etc. How can I modify this to be able to re-insert them into the output? thanks much!
============ UPDATE!
I tried splitting the original stream into "words" and "non-words". It seems to work fine. Not sure whether it's the most efficient way, though:
public ArrayList splitToWords(String sIn)
{
if (sIn == null || sIn.length() == 0) {
return null;
}
char[] c = sIn.toCharArray();
ArrayList<Token> list = new ArrayList<Token>();
int tokenStart = 0;
boolean curIsLetter = Character.isLetter(c[tokenStart]);
for (int pos = tokenStart + 1; pos < c.length; pos++) {
boolean newIsLetter = Character.isLetter(c[pos]);
if (newIsLetter == curIsLetter) {
continue;
}
TokenType type = TokenType.NONWORD;
if (curIsLetter == true)
{
type = TokenType.WORD;
}
list.add(new Token(new String(c, tokenStart, pos - tokenStart),type));
tokenStart = pos;
curIsLetter = newIsLetter;
}
TokenType type = TokenType.NONWORD;
if (curIsLetter == true)
{
type = TokenType.WORD;
}
list.add(new Token(new String(c, tokenStart, c.length - tokenStart),type));
return list;
}
Well it doesn't really lose whitespace, you still have your original text :)
So I think you should make use of OffsetAttribute, which contains startOffset() and endOffset() of each term into your original text. This is what lucene uses, for example, to highlight snippets of search results from the original text.
I wrote up a quick test (uses EnglishAnalyzer) to demonstrate:
The input is:
Just a test of some ideas. Let's see if it works.
The output is:
just a test of some idea. let see if it work.
// just for example purposes, not necessarily the most performant.
public void testString() throws Exception {
String input = "Just a test of some ideas. Let's see if it works.";
EnglishAnalyzer analyzer = new EnglishAnalyzer(Version.LUCENE_35);
StringBuilder output = new StringBuilder(input);
// in some cases, the analyzer will make terms longer or shorter.
// because of this we must track how much we have adjusted the text so far
// so that the offsets returned will still work for us via replace()
int delta = 0;
TokenStream ts = analyzer.tokenStream("bogus", new StringReader(input));
CharTermAttribute termAtt = ts.addAttribute(CharTermAttribute.class);
OffsetAttribute offsetAtt = ts.addAttribute(OffsetAttribute.class);
ts.reset();
while (ts.incrementToken()) {
String term = termAtt.toString();
int start = offsetAtt.startOffset();
int end = offsetAtt.endOffset();
output.replace(delta + start, delta + end, term);
delta += (term.length() - (end - start));
}
ts.close();
System.out.println(output.toString());
}