How in kotlin to initialize a two dimensional non-nullable string array - arrays

Having a two demential string array and the row has only two items,
i.e. {{"1", "a"} {"2", "b"}, {"3", "c"} ... ...}
the invert function to do the invert
public static String[][] invert(final String[][] array) {
final String[][] newarray = new String[array.length][2];
for(int i = 0; i<array.length; i++) {
newarray[i][0] = array[i][1];
newarray[i][1] = array[i][0];
}
return newarray;
}
translate to kotlin:
fun invert(array: Array<Array<String?>>): Array<Array<String?>> {
val newarray = Array<Array<String?>>(array.size) { arrayOfNulls(2) }
for (i in array.indices) {
newarray[i][0] = array[i][1]
newarray[i][1] = array[i][0]
}
return newarray
}
Because of the arrayOfNulls(2), has to define the two demential array to be Array<Array<String?>>
but the return type of Array<Array<String?>> breaks the a lot of the code which expecting Array<Array<String>>
Using the val newarray = Array<Array<String>>(array.size) { Array<String>(2) {""}} to force the array initialized all rows with empty string (so not null).
fun invert(array: Array<Array<String>>): Array<Array<String>> {
val newarray = Array<Array<String>>(array.size) { Array<String>(2) {""}}
for (i in array.indices) {
newarray[i][0] = array[i][1]
newarray[i][1] = array[i][0]
}
return newarray
}
Is it the only way, or is there some function like
fun strinagArrayOf(vararg elements: String): Array<String>

That translation threw you off the track; there's no reason to initialize and loop again through the fields.
This is signature of constructor you're using:
/**
* Creates a new array with the specified [size], where each element is calculated by calling the specified
* [init] function. The [init] function returns an array element given its index.
*/
public inline constructor(size: Int, init: (Int) -> T)
You actually get to pass you own initialization logic for each item in the init function. You even get index of item you're initializing (it's the only argument so it's not explicitly declared but it's accesible as it).
So to get what you need, You can simply initialize inverted array right there (changed param from array to input for readability):
fun invert(input: Array<Array<String>>) = Array(input.size) {arrayOf(input[it][1], input[it][0]) }

Related

Kotlin How to ,,deep" copy array so I have two identical arrays but they are referencing different memory locations (they don't contain same objects)

I need to iterate over an array and change it's objects but I still need to reference the original array so that is why I need copy of the original array to change it's objects and then after completion of my operations I want to change the original array into the (copied) changed one. The size and objects are going to be the same but their values are going to change.
Thank you very much for your time and help in advance.
My problem is: when I copy the original array both copy and original are referencing the same locations in the memory so the state of the object changes and my code cannot complete it's tasks correctly. How can I copy an array so the copy references another locations in the memory but with identical objects?
Simple var copyArray = originalArray obviously doesn't work. I searched the web thorougly and I couldn't find exact answer to my problem in Kotlin.
Option 1:
Since your object only has one level of properties, a shallow copy is as effective as a deep copy. If it had more than one level you could still use copy() method but would have to specify explicitly how to copy each specific property.
val arrayOfArrays2 = arrayOfArrays.map { arrayOfCells -> arrayOfCells.map { it.copy() } }
Full code:
fun main() {
val cell1 = Cell(true)
val cell2 = Cell(false)
val arrayA = arrayOf(cell1, cell2)
val arrayOfArrays = arrayOf(arrayA, arrayA)
val arrayOfArrays2 = arrayOfArrays.map { arrayOfCells -> arrayOfCells.map { it.copy() } }
arrayOfArrays[0][0].isAlive = false;
arrayOfArrays.forEach { it.forEach { println("ArrayA: $it") } }
println()
arrayOfArrays2.forEach { it.forEach { println("ArrayB: $it") } }
}
Which results in:
ArrayA: Cell(isAlive=false)
ArrayA: Cell(isAlive=false)
ArrayA: Cell(isAlive=false)
ArrayA: Cell(isAlive=false)
ArrayB: Cell(isAlive=true)
ArrayB: Cell(isAlive=false)
ArrayB: Cell(isAlive=true)
ArrayB: Cell(isAlive=false)
Option 2:
You can serialize and deserialize the object.
Add the following dependency to build.gradle:
implementation 'com.google.code.gson:gson:2.9.0'
Make a deepCopy function inside your cell class:
import com.google.gson.Gson
data class Cell(
var isAlive: Boolean
) {
fun deepCopy(): Cell {
val json = Gson().toJson(this)
return Gson().fromJson(json, Cell::class.java)
}
}
And code to test it out with:
fun main() {
val cell1 = Cell(true)
val cell2 = Cell(false)
val arrayA = arrayOf(cell1, cell2)
val arrayB = arrayA.map { it.deepCopy() }.toTypedArray()
cell1.isAlive = false
println(arrayA.contentToString())
println(arrayB.contentToString())
}
Output:
[Cell(isAlive=false), Cell(isAlive=false)]
[Cell(isAlive=true), Cell(isAlive=false)]
Also, since you have a 2D array:
fun main() {
val cell1 = Cell(true)
val cell2 = Cell(false)
val arrayA = arrayOf(cell1, cell2)
val arrayOfArrays = arrayOf(arrayA, arrayA)
val arrayOfArrays2 = arrayOfArrays.map { arrayOfCells -> arrayOfCells.map { it.deepCopy() } }.toTypedArray()
arrayOfArrays[0][0].isAlive = false;
arrayOfArrays.forEach { it.forEach { println("ArrayA: $it") } }
println()
arrayOfArrays2.forEach { it.forEach { println("ArrayB: $it") } }
}
Which results in:
ArrayA: Cell(isAlive=false)
ArrayA: Cell(isAlive=false)
ArrayA: Cell(isAlive=false)
ArrayA: Cell(isAlive=false)
ArrayB: Cell(isAlive=true)
ArrayB: Cell(isAlive=false)
ArrayB: Cell(isAlive=true)
ArrayB: Cell(isAlive=false)

I want to add 1 to all elements in array by using for loop in swift. while i am trying adding number is applies to only one element

var objArray = [1,3,4,5,2,3,4,2,3,8,6,7,10,11,23,33]
print(addArrayNumbers())
func addArrayNumbers()-> Int {
var add = 0
for objNum in objArray[0...15] {
add = objNum + 1
}
return add
}
}
Your code doesn't make much sense. You're not changing the contents of your array at all. You are looping through and adding the value of each element (plus one) to a total.
If you want to create a new array that adds one to every element in an existing array, your code might look like this:
let objArray = [1,3,4,5,2,3,4,2,3,8,6,7,10,11,23,33]
func addValue(_ value: Int, toArray array: [Int])-> [Int] {
return array.map { $0 + value }
}
print(addValue(1, toArray: objArray))
If you want to change the array in place, you could use code like this:
var objArray = [1,3,4,5,2,3,4,2,3,8,6,7,10,11,23,33]
func addValue(_ value: Int, toArray array: inout [Int]) {
array.enumerated().forEach { (index, element) in
array[index] += value
}
}
print(objArray)
addValue(1, toArray: &objArray)
print(objArray)
Edit:
If you specifically want to use a for in method, you could rewrite the forEach above as follows:
for (index, element) in array.enumerated() {
array[index] += value
}

How would I create a function that takes an array and returns a two dimensions array?

I am having problems creating a function that accepts an array and returns a two-dimensional array. An an example like [1,2,3,4,5,6] = [[1,2],[3,4],[5,6]].
So far I only have :
func spiltArray(numbers:[Int])->[[Int]]{
}
func spiltArray(numbers:[Int])->[[Int]]{
var result:[[Int]] = []
if numbers.count == 0{
return result
}
let split = 2
var arr:[Int] = []
for item in numbers{
if(arr.count>=split){
result.append(arr)
arr = []
}
arr.append(item)
}
result.append(arr)
return result
}

Reduce a string to a dictionary in Swift

What woudl be a simple way to reduce a string like AAA:111;BBB:222;333;444;CCC:555 to a dictionary in Swift. I have the following code:
var str = "AAA:111;BBB:222;333;444;CCC:555"
var astr = str.componentsSeparatedByString(";").map { (element) -> [String:String] in
var elements = element.componentsSeparatedByString(":")
if elements.count < 2 {
elements.insert("N/A", atIndex: 0)
}
return [elements[0]:elements[1]]
}
The code above produces an Array of Dictionaries:
[["A": "111"], ["BBB": "222"], ["UKW": "333"], ["UKW": "444"], ["CCC": "555"]]
I want it to produce
["A": "111", "BBB": "222", "UKW": "333", "UKW": "444", "CCC": "555"]
but no mater what I try, since i call the map function on an Array it seems impossible to convert the nature of the map function's result.
NOTE: The dictionary in string format is described as either having KEY:VALUE; format or VALUE; format, in which case the mapping function will add the "N/A" as being the key of the unnamed value.
Any help on this matter is greatly appreciated.
Your map produces an array of dictionaries. When you want to combine them into 1, that's a perfect job for reduce:
func + <K,V>(lhs: Dictionary<K,V>, rhs: Dictionary<K,V>) -> Dictionary<K,V> {
var result = Dictionary<K,V>()
for (key, value) in lhs {
result[key] = value
}
for (key, value) in rhs {
result[key] = value
}
return result
}
var str = "AAA:111;BBB:222;333;444;CCC:555"
var astr = str
.componentsSeparatedByString(";")
.reduce([String: String]()) {
aggregate, element in
var elements = element.componentsSeparatedByString(":")
if elements.count < 2 {
elements.insert("N/A", atIndex: 0)
}
return aggregate + [elements[0]:elements[1]]
}
print(astr)
Swift has no default operator to "combine" two Dictionaries so you have to define one. Note that the + here is not commutative: dictA + dictB != dictB + dictA. If a key exist in both dictionaries, the value from the second dictionary will be used.
This is a work for reduce:
let str = "AAA:111;BBB:222;333;444;CCC:555"
let keyValueStrings = str.componentsSeparatedByString(";")
let dictionary = keyValueStrings.reduce([String: String]()) {
aggregate, element in
var newAggregate = aggregate
let elements = element.componentsSeparatedByString(":")
let key = elements[0]
// replace nil with the value you want to use if there is no value
let value = (elements.count > 1) ? elements[1] : nil
newAggregate[key] = value
return newAggregate
}
print(dictionary)
You can also make aggregate mutable directly:
let dictionary = keyValueStrings.reduce([String: String]()) {
(var aggregate: [String: String], element: String) -> [String: String] in
let elements = element.componentsSeparatedByString(":")
let key = elements[0]
// replace nil with the value you want to use if there is no value
let value = (elements.count > 1) ? elements[1] : nil
aggregate[key] = value
return aggregate
}
This is a functional approach, but you can achieve the same using a for iteration.
The reason this is happening is because map can only return arrays. If you are using this method to parse your string, then you need to convert it to a dictionary after.
var newDict = [String:String]()
for x in astr {
for (i, j) in x {
newDict[i] = j
}
}
The current issue with your code is that map function iterates over array containing [["key:value"],["key:value"]..] and you separate it again. But it returns ["key":"value"] which you then add to your array.
Instead you can add elements[0]:elements[1] directly to a locally kept variable which will fix your problem. Something like
finalVariable[elements[0]] = elements[1]

How can I tell what the index of an item in a multidimensional array would be if the array were flattened?

I have a multidimensional array with some repeated elements in the deepest level:
[
["taco","burrito"],
["chalupa","taco","bread"]
["pizza","lasagna"],
["pizza","taco","burrito"]
["salad","sandwich"],
["meat","turkey"]
["cups","chicken"],
["rabbit","taco", "chicken", "salad"]
]
I have flattened this array into...
[
"taco",
"burrito",
"chalupa",
"taco",
"bread",
"pizza",
"lasagna",
etc...
]
I have found the second occurrence of the word "taco" in the multidimensional array and know its index.
How can I translate that index to an index in the flattened array?
So in this example it would be...
multiDimensionalArray[0][1][1]
=
flatArray[3]
Swift doesn't have native support for flattening arrays. You could roll this out yourself, or you could use ExSwift which has a flatten method. If you flatten the array successfully, obviously you could then determine which index the value is at using indexOfObject. Since you have multiple identical objects, you may want to use indexesOfObjectsPassingTest, which will give you an index set for all indexes matching the test you provide.
First of all you need a function to flatten the array into an array of strings in order to gain the true indices of the elements.
public func flattenArray(array: Array<AnyObject>) -> Array<String> {
var flattened: Array<String> = []
for object: AnyObject in array {
if object is Array<AnyObject> {
for string in flattenArray(object as Array<AnyObject>) {
flattened.append(string)
}
} else if object is String {
flattened.append(object as String)
}
}
return flattened
}
Then you need to write a function that will obtain the index of an element in an array at a certain number of occurrences
public func indexOfString(string: String, inArray array: Array<String>, occurance: Int = 1) -> Int? {
var occurancesSoFar = 0
var index: Int? = nil
var currentIndex = 0
for object in array {
if object == string {
if (++occurancesSoFar == occurance) {
index = currentIndex
}
}
++currentIndex
}
return index
}
Calling the above functions in order gives the correct index value:
let array = [
["taco","burrito"],
["chalupa","taco","bread"],
["pizza","lasagna"],
["pizza","taco","burrito"],
["salad","sandwich"],
["meat","turkey"],
["cups","chicken"],
["rabbit","taco", "chicken", "salad"]
]
let flat = flattenArray(array)
let index = indexOfString("taco", inArray: flat, occurance: 2)
println(index) // Optional(3)
Using these methods you could encapsulate them into a single function perhaps called
func flattenAndFindElement(element: String,
inArray array: Array<AnyObject>,
atOccurrence occurrence: Int) -> Int?
or something similar.
Hope that helps answer your question.
Here is what I ended up doing:
func findFlattenedIndex(array: Array<Array<String>>, firstIndex: Int, secondIndex: Int) -> Int {
var flatIndex = 0
for (index1,secondTier) in enumerate(array) {
for element in secondTier {
if index1 != firstIndex{
println(element)
flatIndex += 1
}
}
if index1 == firstIndex {
flatIndex += secondIndex
return flatIndex
break
}
}
return flatIndex
}
Of course this could be modified to deal with any number of dimensions.
Thanks for everyone's help with this.

Resources