Kotlin has many shorthands and interesting features. So, I wonder if there is some fast and short way of converting array of string to array of integers. Similar to this code in Python:
results = [int(i) for i in results]
You can use .map { ... } with .toInt() or .toIntOrNull():
val result = strings.map { it.toInt() }
Only the result is not an array but a list. It is preferable to use lists over arrays in non-performance-critical code, see the differences.
If you need an array, add .toTypedArray() or .toIntArray().
I'd use something simple like
val strings = arrayOf("1", "2", "3")
val ints = ints.map { it.toInt() }.toTypedArray()
Alternatively, if you're into extensions:
fun Array<String>.asInts() = this.map { it.toInt() }.toTypedArray()
strings.asInts()
If you are trying to convert a List structure that implements RandomAccess (like ArrayList, or Array), you can use this version for better performance:
IntArray(strings.size) { strings[it].toInt() }
This version is compiled to a basic for loop and int[]:
int size = strings.size();
int[] result = new int[size];
int index = 0;
for(int newLength = result.length; index < newLength; ++index) {
String numberRaw = strings.get(index);
int parsedNumber = Integer.parseInt(numberRaw);
result[index] = parsedNumber;
}
If you use Array.map as other answers suggest, you get back a List, not an Array. If you want to map an array strings to another array results, you can do it directly like this:
val results = Array(strings.size) { strings[it].toInt() }
This is more efficient than first mapping to a List and then copying the elements over to an Array by calling .toTypedArray().
Consider the input like this "" (empty string)
It would be better to do the filtering first. And it is true the return value is list but not array.
If you need an array, add .toTypedArray() or .toIntArray().
fun stringToIntList(data: String): List<Int> =
data.split(",").filter { it.toIntOrNull() != null }
.map { it.toInt() }
val result = "[1, 2, 3, 4, 5]".removeSurrounding("[","]").replace(" ","").split(",").map { it.toInt() }
Found following simplest
strings.chars().toArray()
Related
I am trying to sort only the first dimension of a two-dimensional array
I have
arr = [a,b,c,a,b,c,a,b,c]
arr1 = arr.sort() --> arr1 = [a,a,a,b,b,b,c,c,c]
result = transpose([arr1,arr])
Which gives
result = [[[a],[a]],[[a],[a]],[[a],[a]],[[b],[b]],[[b],[b]],[[b],[b]],[[c],[c]],[[c],[c]],[[c],[c]]]
But I need (and expected)
result = [[[a],[a]],[[a],[b]],[[a],[c]],[[b],[a]],[[b],[b]],[[b],[c]],[[c],[a]],[[c],[b]],[[c],[c]]]
Thanks
You need to make an actual clone of the array, arr is being sorted in what you are doing.
try this:
arr = [a,b,c,a,b,c,a,b,c]
arr1 = arr.slice(0);
arr1.sort();
result = transpose([arr1,arr])
My testing is limited because transpose isn't a GAS function and you didn't include it.
Actually with, this, it seems to work:
function transpose(a)
{
return Object.keys(a[0]).map(function (c) { return a.map(function (r) { return r[c]; }); });
}
I have an array of strings, examples are as follows:
"0.125-0.25"
"1-2"
"50-100"
"100-200"
The result of sorting these is:
"0.125-0.25"
"1-2"
"100-200"
"50-100"
And if I append("1000-2000") to the array and then sort it will be:
"0.125-0.25"
"1-2"
"100-200"
"1000-2000"
"50-100"
But what I want is:
"0.125-0.25"
"1-2"
"50-100"
"100-200"
"1000-2000"
It's definitely an edge case, but I have been having luck on my own. Thanks everyone.
A working but not very efficient solution is to extract the first Double value in the string ranges and sort by them. It's very inefficient because in each call of the closure both Double values have to be recreated.
var array = ["1-2", "50-100", "0.125-0.25", "100-200"]
array.append("1000-2000")
let sortedArray = array.sorted { (str1, str2) -> Bool in
func firstDouble(of string: String) -> Double { return Double(string.components(separatedBy: "-").first!)! }
return firstDouble(of: str1) < firstDouble(of: str2)
}
print(sortedArray)
A more efficient solution is to map the array (once) to its first Double value, then zip both arrays, sort the combined array by the Double array and map the result back to the string-range array.
var array = ["1-2", "50-100", "0.125-0.25", "100-200"]
array.append("1000-2000")
let firstDoubleArray = array.map{Double($0.components(separatedBy: "-").first!)!}
let sortedArray = zip(array, firstDoubleArray).sorted {$0.1 < $1.1}.map{$0.0}
print(sortedArray)
What it appears you're sorting is ranges of Doubles, so the problem can be clarified by creating an intermediate object…
struct DoubleRange: Comparable {
let start: Double
let end: Double
init(string: String) {
let components = string.split(separator: "-")
start = Double(components[0])! // Be careful with `!` here, I'm assuming the format is always correct
end = Double(components[1])!
}
var stringValue: String {
return "\(start)-\(end)"
}
static func < (lhs: DoubleRange, rhs: DoubleRange) -> Bool {
return lhs.start < rhs.start
}
}
Then sorting is simple…
var array = ["1-2", "50-100", "0.125-0.25", "100-200"]
array.append("1000-2000")
array.map(DoubleRange.init).sorted().map{$0.stringValue}
// ["0.125-0.25", "1.0-2.0", "50.0-100.0", "100.0-200.0", "1000.0-2000.0"]
And if you always want to convert back to the string value, you could add…
extension Array where Element == String {
func sortedDoubleRange() -> [String] {
return array.map(DoubleRange.init).sorted().map{$0.stringValue}
}
}
array.sortedDoubleRange()
Be careful with this though… it will crash if any of strings are formatted incorrectly.
I want to sort my string array based on last character. Here is my string array:
["c_572A267C-DAC8-487D-B1AF-719FE8E3A6AB_FF6E00_2",
"b_69E21DC6-431C-4373-B4F1-90BF7FB5462B_FFC000_1"]
Now I want to sort this array based on last value that is after last underscore(_).
Is it possible ?
Thanks
sorted can provide a custom sort condition for example (assuming that all strings are not empty)
let array = ["c_572A267C-DAC8-487D-B1AF-719FE8E3A6AB_FF6E00_2", "b_69E21DC6-431C-4373-B4F1-90BF7FB5462B_FFC000_1"]
let sortedArray = array.sorted { $0.substring(from: $0.index(before: $0.endIndex)) < $1.substring(from: $1.index(before: $1.endIndex)) }
Swift 3+ the syntax is much more convenient
let array = ["c_572A267C-DAC8-487D-B1AF-719FE8E3A6AB_FF6E00_2", "b_69E21DC6-431C-4373-B4F1-90BF7FB5462B_FFC000_1"]
let sortedArray = array.sorted { $0.suffix(1) < $1.suffix(1) }
No doubt it is. By using sorted(by:), you could do it like this:
let myArray = ["c_572A267C-DAC8-487D-B1AF-719FE8E3A6AB_FF6E00_2",
"b_69E21DC6-431C-4373-B4F1-90BF7FB5462B_FFC000_1"]
let sortedArray = myArray.sorted {
guard let str1LastChar = $0.characters.last, let str2LastChar = $1.characters.last else {
return false
}
return str1LastChar < str2LastChar
}
print(sortedArray)
Note that if myArray contains any empty string ("") the sort should be as is.
One more answer with Higher Order Function:
Reverse Each word in an Array
Then, Sort
let arr = ["c_572A267C-DAC8-487D-B1AF-719FE8E3A6AB_FF6E00_2",
"b_69E21DC6-431C-4373-B4F1-90BF7FB5462B_FFC000_1"]
One Line:
let returnValue = arr.map({String($0.reversed())}).sorted().map({String($0.reversed())})
Multi Line:
let reverseEachWordsArr = arr.map { value in
return String(value.reversed())
}
let finalCharSortArr = reverseEachWordsArr.sorted().map { word in
return String(word.reversed())
}
print(finalCharSortArr)
OUTPUT:
["b_69E21DC6-431C-4373-B4F1-90BF7FB5462B_FFC000_1",
"c_572A267C-DAC8-487D-B1AF-719FE8E3A6AB_FF6E00_2"]
I have the following code in Swift 3:
var numbers = [1,2,1]
for number in numbers.count - 1 { // error
if numbers[number] < numbers[number + 1] {
print(number)
}
}
I am checking if the value on the index [number] is always higher than the value on the index [number + 1]. I am getting an error:
Type Int does not conform to protocol sequence
Any idea?
It may be swift.
You can use this iteration.
for number in 0..<(numbers.count-1)
The error is because Int is not a Sequence. You can create a range as already suggested, which does conform to a sequence and will allow iteration using for in.
One way to make Int conform to a sequence is:
extension Int: Sequence {
public func makeIterator() -> CountableRange<Int>.Iterator {
return (0..<self).makeIterator()
}
}
Which would then allow using it as a sequence with for in.
for i in 5 {
print(i)
}
but I wouldn't recommend doing this. It's only to demonstrate the power of protocols but would probably be confusing in an actual codebase.
From you example, it looks like you are trying to compare consecutive elements of the collection. A custom iterator can do just that while keeping the code fairly readable:
public struct ConsecutiveSequence<T: IteratorProtocol>: IteratorProtocol, Sequence {
private var base: T
private var index: Int
private var previous: T.Element?
init(_ base: T) {
self.base = base
self.index = 0
}
public typealias Element = (T.Element, T.Element)
public mutating func next() -> Element? {
guard let first = previous ?? base.next(), let second = base.next() else {
return nil
}
previous = second
return (first, second)
}
}
extension Sequence {
public func makeConsecutiveIterator() -> ConsecutiveSequence<Self.Iterator> {
return ConsecutiveSequence(self.makeIterator())
}
}
which can be used as:
for (x, y) in [1,2,3,4].makeConsecutiveIterator() {
if (x < y) {
print(x)
}
}
In the above example, the iterator will go over the following pairs:
(1, 2)
(2, 3)
(3, 4)
This maybe a little late but you could have done:
for number in numbers { }
instead of:
for number in numbers.count - 1 { }
For a for loop to work a sequence (range) is needed. A sequence consists of a stating a value, an ending value and everything in between. This means that a for loop can be told to loop through a range with ether
for number in 0...numbers.count-1 { } `or` for number in numbers { }
Both example give the nesasery sequences. Where as:
for number in numbers.count - 1 { }
Only gives one value that could either be the starting or the ending value, making it impossible to work out how many time the for loop will have to run.
For more information see Apple's swift control flow documnetation
This error can also come about if you try to enumerate an array instead of the enumerated array. For example:
for (index, element) in [0, 3, 4] {
}
Should be:
for (index, element) in [0, 3, 4].enumerated() {
}
So first you need to understand what is sequence..
A type that provides sequential, iterated access to its elements.
A sequence is a list of values that you can step through one at a time. The most common way to iterate over the elements of a sequence is to use a for-in loop:
let oneTwoThree = 1...3. // Sequence
for loop actually means
For number in Sequences {}
So you need to use
for number in 0..<(numbers.count-1) {}
The error is because number is not an index, but the element of the array on each iteration. You can modify your code like this:
var numbers = [1,2,1,0,3]
for number in 0..<numbers.count - 1 {
if numbers[number] < numbers[number + 1] {
print(numbers[number])
}
}
Or there is a trick using the sort method, but that's kind of a hack (and yes, the subindexes are right, but look like inverted; you can try this directly on a Playground):
var numbers = [1,2,1,0,3]
numbers.sort {
if $0.1 < $0.0 {
print ($0.1)
}
return false
}
For me, this error occurred when I tried writing a for loop, not for an array but a single element of the array.
For example:
let array = [1,2,3,4]
let item = array[0]
for its in item
{
print(its)
}
This gives an error like: Type Int does not conform to protocol 'sequence'
So, if you get this error in for loop, please check whether you are looping an array or not.
How do I get the index of a value from a Kotlin array?
My best solution right now is using:
val max = nums.max()
val maxIdx = nums.indices.find({ (i) -> nums[i] == max }) ?: -1
is there a better way?
If you want to get the index of the maximum element you can use 'maxBy' function:
val maxIdx = nums.indices.maxBy { nums[it] } ?: -1
It is more efficient since it will traverse the array only once.
With current Kotlin (1.0) you can use indexOf() extension function on arrays:
val x = arrayOf("happy","dancer","jumper").indexOf("dancer")
All extension functions for arrays are found in the api reference.
In your example:
val maxIdx = nums.indexOf(nums.max())
If you want to find the item based on some predicate, then you can use indexOfFirst and indexOfLast extension functions.
val strings = arrayOf("hello","world","hello")
val firstHelloIndex = strings.indexOfFirst { it == "hello" }
val lastHelloIndex = strings.indexOfLast { it == "hello" }