Given an array that contains elements like this:
let array = [[a], [b, c], [d, e, f]]
Is there an elegant way to convert this array to an array which returns a tuple with the index of the outer array:
let result = [(a, 0), (b, 1), (c, 1), (d, 2), (e, 2), (f, 2)]
let array = [["a"], ["b", "c"], ["d", "e", "f"]]
let result = zip(array, array.indices).flatMap { subarray, index in
subarray.map { ($0, index) }
}
result is:
[("a", 0), ("b", 1), ("c", 1), ("d", 2), ("e", 2), ("f", 2)]
I used zip(array, array.indices) instead of array.enumerated() because you specifically asked for a tuple with the array index — enumerated() produces tuples with zero-based integer offsets. If your source collection is an array, it doesn't make a difference, but other collections (like ArraySlice) would behave differently.
Related
I am looking for an idiomatic method in Kotlin (JVM) to flatten a 2-dimensional list. But, unlike the existing .flatten(), I would like to retain the individual indices, similar to .withIndex(), but 2-dimensional.
Example:
// Before
[
["a", "b", "c"],
["d", "e"],
["f", "g", "h", "i"]
]
// Afterwards, something like
[
(0, 0, "a"), (0, 1, "b"), (0, 2, "c"),
(1, 0, "d"), (1, 1, "e"),
(2, 0, "f"), (2, 1, "g"), (2, 2, "h"), (2, 3, "i")
]
flattenWithIndex
Unsure whether there is an idiomatic approach to this already somewhere in the lib, but it is fairly easy to write this yourself with a bunch of nested .withIndex() and some (flat)-mapping:
// inspired by IndexedValue for 1D
data class GridValue<out T>(val rowIndex: Int, val colIndex: Int, val value: T)
// similar to flatten().withIndex() for 1D
fun <T> Iterable<Iterable<T>>.flattenWithIndex(): List<GridValue<T>> =
withIndex().flatMap { (rowIndex, row) ->
row.withIndex().map { (colIndex, value) ->
GridValue(rowIndex, colIndex, value)
}
}
withGridIndex
As a little bonus, here is the 2D variant of withIndex():
// only difference is map vs flatMap
fun <T> Iterable<Iterable<T>>.withGridIndex(): List<List<GridValue<T>>> =
withIndex().map { (rowIndex, row) ->
row.withIndex().map { (colIndex, value) ->
GridValue(rowIndex, colIndex, value)
}
}
Notes
As usual, substitute with sequences if necessary.
Technically, flattenWithIndex() could also be rewritten as
fun <T> Iterable<Iterable<T>>.flattenWithIndex(): List<GridValue<T>> =
withGridIndex().flatten()
But that would create unecessary lists in the process.
As you describe question is actually do type conversion like: List<List<String>> to List<Triple<Int, Int, String>>
Generalization: List<List<T>> to List<Triple<Int, Int, T>>
and code is:
fun <T> List<List<T>>.flattenWithIndex(): List<Triple<Int, Int, T>> {
val result = mutableListOf<Triple<Int, Int, T>>()
forEachIndexed { i, tList ->
tList.forEachIndexed { j, t ->
result.add(Triple(i, j, t))
}
}
return result
}
val list = listOf(listOf("a", "b", "c"), listOf("d", "e"), listOf("f", "g", "h", "i"))
println(list.flattenWithIndex())// [(0, 0, a), (0, 1, b), (0, 2, c), (1, 0, d), (1, 1, e), (2, 0, f), (2, 1, g), (2, 2, h), (2, 3, i)]
This question already has answers here:
In Swift how can I sort one array based on another array?
(4 answers)
Closed 4 years ago.
I want to sort one array and "mirror" order changes to another array of the same dimension.
The question is similar to this one but for Swift language:
Better way to sort 2 "linked" arrays?
Example:
let arr1 = ["a", "b", "c", "d", "e"]
let arr2 = [1, 5, 9, 2, 3]
... //sort
//result == ["a", "d", "e", "b", c"]
Approximate solution I know:
for i in stride(from 0, to: arr2.count - 1, by: 1) {
for j in stride(from i + 1, to: arr2.count, by: 1) {
if arr2[i] > arr2[j] {
...//swap arr2[i] and arr2[j]
...//swap arr1[i] and arr1[j]
}
}
}
But they added a lot of additional possibilities for advanced working with arrays in Swift. So is it possible to simplify this solution with inner Swift features?
Note: arr1 and arr2 are given as separate arrays.
EDITED
Yes. You found the similar questions but their titles are awful and don't reflect the answers their authors need. In other words if you delete/close my question the people may continue to ask it again because it is impossible to find something with existing titles!
zip the arrays together
sort the new array by array 2
map the array to array 1
let arr1 = ["a", "b", "c", "d", "e"]
let arr2 = [1, 5, 9, 2, 3]
let result = zip(arr1, arr2) // [("a", 1), ("b", 5), ("c", 9), ("d", 2), ("e", 3)]
.sorted(by: {$0.1 < $1.1}) // [("a", 1), ("d", 2), ("e", 3), ("b", 5), ("c", 9)]
.map{ $0.0 } // ["a", "d", "e", "b", "c"]
I am trying to make an array of arrays where each nested array has a string and an integer.
I have seen you can use structs but all I want to do is make them a constant, and I want to know if there is a way to do it without having to type loads of extra stuff
let items: [[String, Int]] = [["A", 1], ["B", 2], ["C", 3]]
I think what you want is an array of tuples, not an array of arrays. That implementation would look like this:
let items: [(String, Int)] = [("A", 1), ("B", 2), ("C", 3)]
You could access these properties like this:
let itemOneString = items[0].0 // "A"
let itemOneInt = items[0].1 // 1
It will work for you:
let items: [[(String, Int)]] = [[("A", 1)], [("B", 2)], [("C", 3)]]
Array is collection of similar data types. It can not contain heterogeneous types data.
But if you still want to do it. There are other workarounds like create array of dictionary like this.
let items: [[String: Any]] = [["String" : "A", "Int" : 1], ["String" : "B", "Int" : 2]]
or create an array of Tuples.
let items: [(String, Int)] = [("A", 1), ("B", 2), ("C", 3)]
You can add any number of items in Tuple or Dictionary.
Scala newbie, Have an array where one element is an array:
val aaa = Array("a", "b", Array(1, 2, 3), "c")
This works:
In []: aaa(2)
Out[]: Array(1, 2, 3)
This works:
In []: Array(1, 2, 3).size
Out[]:3
This does not:
In []: aaa(2).size
Out[]:
Name: Compile Error
Message: <console>:15: error: value size is not a member of
java.io.Serializable
aaa(2).size
^
What am I doing wrong?
Thanks
When you create an array using the following literal
val aaa = Array("a", "b", Array(1, 2, 3), "c")
Since the type of the elements are different, the array aaa type is created with java.io.Serializable
aaa: Array[java.io.Serializable] = Array(a, b, Array(1, 2, 3), c)
So when you refer back the 2nd element, the type of the reference will be of Serializable and there is no size property in it. So we need to explicity typecast/convert the 2nd element to Array using asInstanceOf. As shown below
if (aaa(2).isInstanceOf[Array[Int]])
aaa(2).asInstanceOf[Array[Int]].size
Most common type for your declaration is serializable
val aaa = Array("a", "b", Array(1, 2, 3), "c")
Array[java.io.Serializable]
If you want to use it with size, you can explicitly define:
val aaa: Array[Seq[Any]] = Array("a", "b", Array(1, 2, 3), "c")
all Strings will be converted to Sequences of Chars in this case.
As mentioned in the comments, it is not a good idea to mix arrays and non-arrays (and, in general, elements of different types) in an array. Sometimes, there are corner cases, when you can't get around having to do that, but as a rule, arrays (and other scala containers) are meant to hold homogenous types.
So, I would recommend to begin with splitting your array into two:
val (arrays, nonArrays) =
Array("a", "b", Array(1, 2, 3), "c").partition {
case a: Array[_] => true
case _ => false
}
Now, you can easily tell the lengths of all your arrays:
arrays.foreach { println(_.size) }
If you wanted to preserve the original position information, you could zip the original array with indexes first:
val (arrays, nonArrays) = Array("a", "b", Array(1, 2, 3), "c")
.zipWithIndex
.partition {
case (a: Array[_], _) => true
case _ => false
}
arrays.foreach {
case (array, index) =>
prinlnt(s"Array length at index $index is ${array.size}")
}
How do you convert from an Array[Int] of length n, into an Array[(Int,Option[Int])] of length Math.ceil(n / 2.0).toInt?
scala> val a = Array(1, 2, 3, 4)
a: Array[Int] = Array(1, 2, 3, 4)
The resultant Array for the example above would be Array((1, Some(2)), (3, some(4))). If a were Array(1), then the desired result would be Array((1, None)).
Capice?
grouped is useful for breaking the array into pairs, and case is useful for dealing with the possibility of a leftover element:
def toPairs[T](xs: Array[T]): Array[(T, Option[T])] =
xs.grouped(2)
.map{
case Array(a, b) => (a, Some(b))
case Array(a) => (a, None)
}.toArray
scala> toPairs(Array(1, 2, 3, 4, 5))
res0: Array[(Int, Option[Int])] = Array((1,Some(2)), (3,Some(4)), (5,None))
Something similar to Seth's suggestion, just a little more concise.
scala> Array(1,2,3,4,5).grouped(2).map(x=> (x.head, x.tail.headOption)).toArray
res17: Array[(Int, Option[Int])] = Array((1,Some(2)), (3,Some(4)), (5,None))