So I have an array of strings in Scala called pronunciationArray like this:
["EH1","N", "D", "P", "ER0", "EH1", "N", "TH", "AH0", "S", "IY2", "Z"]
and i want to write an if else statement that reads the array in reverse, and as soon as it finds a string with a number in it, it either removes all the strings after or puts everything before into a separate array.
so for the above example, id want it to stop at "IY2", then either create a new array with only ["IY2", "Z"] or remove every string after and leave the original array with, like I said, ["IY2", "Z"]. The number itself isnt an integer btw, its part of the string, and the numbers range from 0-2.
I've tried for loop in reverse with an if else that looks for the numbers 0, 1, 2 but it returns every string with a number, so it returns [IY2, AH0, EH1, ER0, EH1] it doesnt stop as soon as it finds the first string with a number. And i'm not sure how to put everything before that string into a new array if I even found a way to stop it.
for (sounds <- pronunciationArray.reverse) {
val number0 = sounds.contains("0")
if (number0 == true) {
println(sounds)
} else if (number0 == true){
println(sounds)
} else if (number1 == true){
println(sounds)
} else {
-1
}
}
I want it to return just ["IY2", "Z"] but it returned [IY2, AH0, EH1, ER0, EH1]
One of the algorithm you can solve with is
find the index of element from the last where element has 0, 1 or 2
split the array at above index
take the last element of first array and whole second array
Example,
val pronunciationArray = Array("EH1","N", "D", "P", "ER0", "EH1", "N", "TH", "AH0", "S", "IY2", "Z")
def takeUntil(inputArray: Array[String], condition: String => Boolean): Array[String] = {
val findIndexFromLast = inputArray.reverse.zipWithIndex.collectFirst {
case (elem, index) if condition.apply(elem) => index
}
findIndexFromLast match { // pattern match to check if there exists element with 0, 1, or 2
case Some(indexFromLast) =>
inputArray.splitAt(inputArray.length - indexFromLast) match {
case (head, tail) => head.last +: tail
}
case None => // no element with 0, 1, 2 exists
inputArray
}
}
takeUntil(
inputArray = pronunciationArray,
condition = elem => elem.contains("0") || elem.contains("1") || elem.contains("2")) //gives [IY2, Z]
The other way to solve the same using .span(predicate) which is better version of .splitAt(index)
def takeUntil2(inputArray: Array[String]): Array[String] = {
inputArray.reverse.span {
element =>
!element.contains("0") && !element.contains("1") && !element.contains("2")
} match {
case (head, tail) => tail.take(1) ++ head.reverse
}
}
val result = takeUntil2(inputArray = pronunciationArray)
Now, using scala way you can extend Array to have custom function,
implicit class ArrayOps(array: Array[String]) {
def takeUntil(predicate: String => Boolean): Array[String] = {
val splitAt = array.reverse.span(predicate.apply)
splitAt._2.take(1) ++ splitAt._1.reverse
}
}
val res = pronunciationArray
.takeUntil(predicate = elem => !elem.contains("0") && !elem.contains("1") && !elem.contains("2"))
Similar question:
How to implement 'takeUntil' of a list?
How do I pattern match arrays in Scala?
I'm not completely clear on your objective, but Array has a span function that splits it into two pieces based on a condition--the first piece is a prefix of the array where everything satisfies the condition and the second piece is the remainder.
Let's say your condition was that the string contains 2 rather than contains a number (just because I'm lazy--it could be any function f: String => Boolean). If we reverse the array and then split on the negation of the function, we get:
scala> a.reverse.span(!_.contains("2"))
res5: (Array[String], Array[String]) = (Array(Z),Array(IY2, S, AH0, TH, N, EH1, ER0, P, D, N, EH1))
We know that the first element of the second array satisfies our condition, so we can just assemble the result:
scala> val (start, end) = a.reverse.span(!_.contains("2"))
start: Array[String] = Array(Z)
end: Array[String] = Array(IY2, S, AH0, TH, N, EH1, ER0, P, D, N, EH1)
scala> val result = end.take(1) ++ start.reverse
result: Array[String] = Array(IY2, Z)
All the reversing is perhaps not the most efficient, but gets the job done.
Scala arrays offer a lastIndexWhere method which takes a predicate and returns the index of the last element which fulfills the predicate:
val arr = Array("EH1","N", "D", "P", "ER0", "EH1", "N", "TH", "AH0", "S", "IY2", "Z")
val idx = arr.lastIndexWhere(e => e.intersect(Array('0', '1', '2')).size > 0)
and then you call the takeRight method to get the desired result
val res = arr.takeRight(arr.size - idx)
Related
Okay so my current code works, but I have a feeling it's incredibly inefficient. Essentially, I need to evaluate if a String contains the letters of a String that is shorter or the same length as the first one. (Imagine trying to use the letters that exist in Word A to spell a new word Word B. Word B can be shorter than Word A or the same length but has to only use the letters from Word A and cannot use the same letter twice.)
My current solution is to sort both strings into an array, then index each letter in the Word B array, check if it appears in the Word A array, then remove that character from the Word A array.
let wordOne = "battle"
let wordTwo = "table"
var wordOneSorted = wordOne.sorted()
var wordTwoSorted = wordTwo.sorted()
for letter in wordTwoSorted {
if wordOneSorted.contains(letter) {
print("Valid Letter")
let idx = wordOneSorted.firstIndex(of:letter)
wordOneSorted.remove(at: idx!)
} else {
print("Invalid Letter")
}
}
Prints:
Valid Letter
Valid Letter
Valid Letter
Valid Letter
Valid Letter
This works but it feels clunky and I wanted to see if I'm making a simple task more complicated than I need it to be. I only need an evaluation of the entire comparison, if all the leters work than "True" and if at least one is invalid than "False".
Thank you!
Your code can give a simple good/bad response as follows:
let wordOne = "battle"
let wordTwo = "table"
var letters = wordOne
var good = true
for letter in wordTwo {
if letters.contains(letter) {
let idx = letters.firstIndex(of:letter)
letters.remove(at: idx!)
} else {
good = false
break
}
}
print(good ? "Good" : "Bad")
There's no need to sort the letters of each word. That doesn't make this approach any more efficient. I add the var letters just so the value can be modified as the loop runs.
Here's an alternate approach using NSCountedSet. This isn't a pure Swift class but is provided by Foundation.
let wordOne = "battle"
let wordTwo = "table"
let set1 = NSCountedSet(array: Array(wordOne))
let set2 = NSCountedSet(array: Array(wordTwo))
let extra = set2.filter { set2.count(for: $0) > set1.count(for: $0) }
print(extra.isEmpty ? "Good" : "Bad")
NSCountedSet is a subclass of Set (really of NSSet and NSMutableSet) that adds a count for each element in the set.
The filter makes sure there are enough of each letter. Anything left in extra means wordTwo had more instances of a letter than in wordOne.
As pointed out, using allSatisfy, rather than filter, would be more efficient (though trivial on such short words). Change the last two lines to:
let good = set2.allSatisfy { set2.count(for: $0) <= set1.count(for: $0) }
print(good ? "Good" : "Bad")
I was wondering how I would, and here is an alternative. It's good to have different ways of thinking an issue.
We create a [Character: (Int, Int)] where - keys will be the letter, and first tuple value the number of occurences in str1, and second tuple value the number of occurrences in str2.
Then, we check if all values present in str1 have at leas the same number of occurrences that in str2.
var counter: [Character: (Int, Int)] = [:]
counter = str1.reduce(counter) {
var values = $0
var tuple: (Int, Int) = $0[$1, default: (0, 0)]
values[$1] = (tuple.0 + 1, tuple.1)
return values
}
counter = str2.reduce(counter) {
var values = $0
var tuple: (Int, Int) = $0[$1, default: (0, 0)]
values[$1] = (tuple.0, tuple.1 + 1)
return values
}
let doesInclude = counter.allSatisfy { _, tuple in
tuple.0 >= tuple.1
}
print("\(str1) includes all letters from \(str2): \(doesInclude)")
The value of counter for "abc" vs "cde":
["d": (0, 1), "b": (1, 0), "c": (1, 1), "e": (0, 1), "a": (1, 0)]
The value of counter for "battle" vs "table":
["t": (2, 1), "e": (1, 1), "a": (1, 1), "b": (1, 1), "l": (1, 1)]
I like #HangarRash answer, but you could also use sort to your advantage, i.e. when letters are sorted you can move in both arrays simultaneously and stop as soon as the first difference was found (no need to remove anything):
func isContained(in word1: String, word word2: String) -> Bool {
var word1Sorted = word1.sorted()
var word2Sorted = word2.sorted()
var c1 = word1Sorted.count - 1
var c2 = word2Sorted.count - 1
while c2 >= 0 && c1 >= 0 {
if word1Sorted[c1] == word2Sorted[c2] {
// Found a match - advance to next character in both arrays
print("Valid Letter \(word2Sorted[c2])")
c1 -= 1
c2 -= 1
} else if word1Sorted[c1] > word2Sorted[c2] {
// Ignoring a letter present in wordOne, but not wordTwo
print("Skipping Letter \(word1Sorted[c1])")
c1 -= 1
} else { // wordOneSorted[c1] < wordTwoSorted[c2]
// the letter was not found in wordOneSorted - no need to continue
print("Invalid Letter \(word2Sorted[c2])")
return false
}
}
// If we finished the loop then the result is:
// - We've got to the beginning of word2, meaning we found all letters of it in word1
// - Or we've got to the beginning of word1, meaning not all letters of word 2 were found
return c2 < 0
}
So for example:
let wordOne = "battle"
let wordTwo = "table"
let good = isContained(in: wordOne, word: wordTwo)
print(good ? "Good" : "Bad")
will run on entire array (of the Word 2 at least):
Valid Letter t
Skipping Letter t
Valid Letter l
Valid Letter e
Valid Letter b
Valid Letter a
Good
While if there's a difference, e.g.
let wordOne = "battle"
let wordTwo = "tables"
let good = isContained(in: wordOne, word: wordTwo)
print(good ? "Good" : "Bad")
it may exit much faster:
Valid Letter t
Skipping Letter t
Invalid Letter s
Bad
I am trying to capitalize every even numbered placeholder in a string in Swift. So the character in [0],[2],[4],[6] all get uppercased.
I have a declared variable:
var str = "This is a test"
I have an array that explodes the variable into an array:
let characters = Array(str) //Creates the array "["T", "h", "i", "s", " ", "i", "s", " ", "a", " ", "t", "e", "s", "t"]\n"
On top of that, I am creating an empty array to input the newly capitalized/lowercased letters
var newCharacters = Array<Character>()
And then declaring index at 0
var index = 0 //declaring the index at 0
I am trying to figure out how to create the for loop that will sniff out the even numbered array item and then capitalize the character found in it.
I have created a for loop that will manipulate the even numbered placeholders in the array, I just do not know the syntax to capitalize the string of every other one:
for letter in characters {
if index % 2 == 0 {
}
}
I am trying to figure out: what the syntax is to capitalize every other letter (even numbers in the array), put them in the new array, and then convert it back to a string.
The end result should be:
"ThIs iS TeSt"
You can combine enumerated, map, and joined in sequence to create the result:
let str = "This is a test"
let result = str.enumerated()
.map { $0.offset % 2 == 0 ? String($0.element).uppercased() : String($0.element) }
.joined()
print(result)
ThIs iS A TeSt
Explanation:
A String can be enumerated as if it were an array of Character. Calling .enumerated() on a String causes it to produces a sequence of (offset: Int, element: Character) tuples.
map takes a sequence and creates an array. The closure following map is called for each element of the sequence in turn and the value that the closure returns becomes the next element in the new array.
$0 is the default name for the value passed to map. In this case, we're passing the (offset: Int, element: Character) tuple. $0.offset is the position of the character in the String, and $0.element is the character.
The ternary operator is used here to return the uppercased() String that is created from the Character if the offset is even or just the String if the offset is odd.
The result of the map is [String], and joined() is then used to join the array of String back into a single String.
One way is using stride:
var str = "This is a test"
var chars = Array(str).map { String($0) }
for i in stride(from: 0, to: chars.count, by: 2) {
chars[i] = chars[i].uppercased()
}
var hiphopcasedStr = chars.joined()
Note that while you're in Unicode land, some characters uppercase to multicharacter strings, so array of Character is not quite appropriate (thus the conversion to array of String).
let str = "This is a test"
let result = str.indices.map {
str.distance(from: str.startIndex, to: $0) % 2 == 0 ?
str[$0...$0].uppercased() :
str[$0...$0].lowercased() }.joined()
print(result) // "ThIs iS A TeSt\n"
or as a mutating method on StringProtocol:
extension StringProtocol where Self: RangeReplaceableCollection {
mutating func capitalizeEveryOther() {
indices.forEach {
replaceSubrange($0...$0, with:
distance(from: startIndex, to: $0) % 2 == 0 ?
self[$0...$0].uppercased() :
self[$0...$0].lowercased() )
}
}
var capitalizingEveryOther: Self {
var result = self
result.capitalizeEveryOther()
return result
}
}
var str5 = "This is a test"
str5.capitalizeEveryOther()
print(str5) // ThIs iS A TeSt
try this code
var str = "This is a test"
var result = [String]()
for (index,element) in str.enumerated() {
var val:String = String(element).lowercased()
if index % 2 == 0 {
val = String(element).uppercased()
}
result.append(val)
}
print(result.joined())
result
ThIs iS A TeSt
I am given a list of strings and I need to see if they are in alphabetical order.
I know I need to use a for loop and check the first character of each string but I don't know how to progress from there.
for (item in array)
println(item[0])
For example ["adam", "ben", "chloe"] should return true.
And likewise for ["ben", "adam", "chloe"] should return false.
UPD: Since Kotlin 1.2 the following is available:
Most efficient variant, creates the smallest amount of intermediate objects:
listOf(1, 2, 3).asSequence().zipWithNext { a, b -> a <= b }.all { it }
Note on efficiency: Creates only two Sequence objects and one function object. Uses Boolean constants as an intermediate value. The final all invocation is inlined as a while loop over the sequence.
Slightly less efficient variant:
listOf(1, 2, 3).asSequence().windowed(2).all { (a, b) -> a <= b }
It's slightly less efficient, as it creates creates intermediate List(a, b) for every element of the original list.
Both of these variants were listed in the answers below by #Billbucket and #AshishChaudhary.
Old answer, for previous versions of Kotlin:
Here is a one liner:
val a = listOf("a", "b", "c")
a.zip(a.drop(1)).all { (a, b) -> a <= b }
// true
Explanation:
a.zip(a.drop(1)) returns pairs of neighbour elements. If in every pair first element is less or equal to the next, array is in order.
If you want to improve performance, you can wrap your array in lazy sequence first. In this case array won't be copied:
a.asSequence().let { it.zip(it.drop(1)).all { (a, b) -> a < b } }
The whole thing is O(N) (single pass through array), which is optimal for this task.
you can use a simple one-liner:
array.zipWithNext { s1, s2 -> s1 <= s2 }.all { it }
Im sure you could do your desired task completely using a for-loop.
However in Kotlin I personally think it would be more idiomatic to do something like this using until:
fun isAlphabetical(stringsArray: Array<String>): Boolean {
if (stringsArray.size < 2) return true
return (1 until stringsArray.size).none { stringsArray[it] <= stringsArray[it - 1] }
}
fun main(args: Array<String>) {
val testCases = arrayOf(arrayOf("adam", "ben", "chloe"), arrayOf("ben", "adam", "chloe"))
for(testCase : Array<String> in testCases){
println("The strings are: ${testCase.joinToString()}")
if (isAlphabetical(testCase)) {
println("Strings are in alphabetical order.\n")
} else {
println("Strings are not in alphabetical order.\n")
}
}
}
Output:
The strings are: adam, ben, chloe
Strings are in alphabetical order.
The strings are: ben, adam, chloe
Strings are not in alphabetical order.
Basically you only compare each element of the array with the previous element (using <=) if the length of the array is more than 1.
A very simple and easy way to accomplish it is by sorting and comparing the original list with the sorted one (two lists are equal when they have the exact same elements in the same order). Since you mentioned you are dealing with an array, you first need to convert it to a list. This solution is not the best in terms of performance (it's O(n log n) and converts the array twice to a list) but it's very readable:
val test = array.asList() == array.asList().sorted()
The full code for your question could be:
println(if (array.asList() == array.asList().sorted()) "Strings are in alphabetical order." else "Strings are not in alphabetical order.")
Another solution:
val list = listOf("a", "b", "c")
list.windowed(2).none { (a, b) -> a > b }
// true
In case you want to compare arbitrary Comparable list:
fun <T : Comparable<T>> isCollectionSortedAsc(list: Collection<T>): Boolean {
return list.zipWithNext { a, b -> a.compareTo(b) == -1 }.all { it }
}
Based on the accepted answer above: https://stackoverflow.com/a/47296632/4919972
Yet another solution :)
data class Result(val isInOrder: Boolean, val lastString: String) {
val toString = when {
isInOrder -> "Strings are in alphabetical order."
else -> "Strings are not in alphabetical order."
}
}
fun Array<String>.isInAlphabeticalOrder() =
this.fold(Result(true, ""), { acc, word -> Result(acc.isInOrder && acc.lastString < word, word) })
fun main(args: Array<String>) {
val test1 = arrayOf("adam", "ben", "chloe")
val test2 = arrayOf("ben", "adam", "chloe")
println(test1.isInAlphabeticalOrder().toString)
println(test2.isInAlphabeticalOrder().toString)
}
So say I have an array:
var stringArray = ["a","b","c","d","e","f","g","h","i","j"]
Now, how do I delete "a", "c", "e", "g", and "i" (all the even number indexes from the array)?
Thanks!
Instead of using C-style for-loops (which are set to be deprecated in an upcoming version of Swift), you could accomplish this using strides:
var result = [String]()
for i in stride(from: 1, through: stringArray.count - 1, by: 2) {
result.append(stringArray[i])
}
Or for an even more functional solution,
let result = stride(from: 1, to: stringArray.count - 1, by: 2).map { stringArray[$0] }
Traditional
var filteredArray = []
for var i = 1; i < stringArray.count; i = i + 2 {
filteredArray.append(stringArray[i])
}
Functional alternative
var result = stringArray.enumerate().filter({ index, _ in
index % 2 != 0
}).map { $0.1 }
enumerate takes a array of elements and returns an array of tuples where each tuple is an index-array pair (e.g. (.0 3, .1 "d")). We then remove the elements that are odd using the modulus operator. Finally, we convert the tuple array back to a normal array using map. HTH
There are a bunch of different ways to accomplish this, but here are a couple that I found interesting:
Using flatMap() on indices:
let result: [String] = stringArray.indices.flatMap {
if $0 % 2 != 0 { return stringArray[$0] }
else { return nil }
}
Note: result needs to be defined as a [String] otherwise the compiler doesn't know which version of flatMap() to use.
Or, if you want to modify the original array in place:
stringArray.indices.reverse().forEach {
if $0 % 2 == 0 { stringArray.removeAtIndex($0) }
}
In this case you have to call reverse() on indices first so that they're enumerated in reverse order. Otherwise by the time you get to the end of the array you'll be attempting to remove an index that doesn't exist anymore.
Swift 4.2
A function accepting generics and producing reduced result
func stripElements<T>(in array:[T]) -> [T] {
return array.enumerated().filter { (arg0) -> Bool in
let (offset, _) = arg0
return offset % 2 != 0
}.map { $0.element }
}
I'm trying to search a nested array of characters for a specific character, and then return the indices of the character from the array.
Code Snippet
def search(target: Char, arr:Array[Array[Char]]): List[Int] = {
for (i <- 0 until arr.length) { //search through first layer of array
for (j <- 0 until arr(i).length) { //search through second layer of array
if (arr(i)(j) == target) {
val x = List(i,j)
return x
} }}}
However, I'm getting an error from compilation, that says this function is returning the two type signature. Error message:
error: type mismatch;
found : Unit
required: List[Int]
for (i <- 0 until arr.length) { //search through first layer of array
^
I've found two similar threads here: found Unit: required Int. Why is the error not obvious?
and found: Unit required: Int - How to correct this?
but they don't solve the problem i'm facing: I am trying to return the List, but the compiler is getting stuck at the for loop..
Seems like there are a lot of answers already, but I think that this is the most idiomatic way to approach the problem:
The Code
def search(target: Char, arr: Array[Array[Char]]): List[(Int, Int)] = {
val indices = for{
(a, i) <- arr.iterator.zipWithIndex
(c, j) <- a.iterator.zipWithIndex
if( c == target )
} yield i -> j
indices.toList
}
The Explanation
In scala, for-comprehensions are nestable, so you can take care of any degree of nested arrays by simply adding another x <- y line. You can introduce filtering with an if statement inside of the for{...}.
In the comprehension, a is the ith array inside arr, where i is the first index. c is the jth character inside a, where j is the second index. I use iterator so that indices can be evaluated on the fly, without needing to copy the arrays behind the scenes because of the for-comprehension. At the end, I call toList to evaluate the results of the indices iterator into a list.
The return type, List[(Int, Int)] is a list of pairs. It makes more sense to return List( (1,2), (3,4) ) if you found your target at i=1, j=2 and i=3, j=4 than to return List(1,2,3,4).
General Thoughts
Try to avoid using return in scala. You can usually handle your collections with an iterator-like approach, using yield, and then evaluate the result by calling a toList or toMap or toWhatever.
The scala collections API is very helpful for many of these cases, too. In the case where you just want the first item that matches a condition, you can use myCollection.find(...). Explore the scala docs to see the huge variety of convenient functions that are already available to you.
I would suggest using a less Java-like approach altogether. I'm not entirely sure what your function is supposed to do, but if you want a list of all (x, y) indices of the match within the nested array, you could do something like this:
def search(target: Char, array: Array[Array[Char]]): Seq[(Int, Int)] = {
array.zipWithIndex.filter(_._1.contains(target)).map { xa =>
xa._1.zipWithIndex.filter(_._1 == target).map(xb => (xa._2, xb._2)).toSeq
}.flatten.toSeq
}
which behaves like this:
val t = Array(
Array('a', 'b', 'c'),
Array('b'), Array('c', 'a'),
Array('a', 'a', 'x', 'a')
)
println(search('a', t))
=> ((0,0), (2,1), (3,0), (3,1), (3,3))
Here is my solution to find the first index of a element in a two-dimension array:
(replace collectFirt to collect, if you wan to find all indexes)
def search[T](target: T, arr: Array[Array[T]]): List[Int] =
arr.indices.collectFirst{
case k if arr(k).contains(target) =>
List(k, arr(k).indexWhere(_ == target))
}.getOrElse(Nil)
Test:
scala> val t = Array(
| Array('a', 'b', 'c'),
| Array('b'),
| Array('c', 'a'),
| Array('a', 'a', 'x', 'a')
| )
scala> println(search('a', t))
List(0, 0)
scala> println(search('x', t))
List(3, 2)
scala> println(search('e', t))
List()
You only return a List if a particular condition is met (arr(i)(j) == target). You have to define a return value for the case that the for comprehensions run through. E.g.
def search(target: Char, arr:Array[Array[Char]]): List[Int] = {
for (i <- 0 until arr.length) { //search through first layer of array
for (j <- 0 until arr(i).length) { //search through second layer of array
if (arr(i)(j) == target) {
val x = List(i,j)
return x
}
}
}
Nil // not found
}
The for loop itself would return Unit if the second array is empty or the if expression evaluates to false. You could rewrite it so it returns null if it nevers gets to the code after the if-expression.
I would leave out the assignment of x too, there's really no point.
def search(target: Char, arr:Array[Array[Char]]): List[Int] =
{
for (i <- 0 until arr.length)
{ //search through first layer of array
for (j <- 0 until arr(i).length)
{ //search through second layer of array
if (arr(i)(j) == target)
{
return List(i,j)
}
}
}
Nil
}
Btw this could probably be rewritten with a more functional approach but that goes beyond the scope of this question.