swift array storing data - arrays

I made a function that returns multiple values
let interestingNumbers = [ // String:Array<Int>
"Prime": [2, 3, 5, 7, 11, 23],
"Fibonacci": [1, 1, 2, 3, 5, 80],
"Square": [1, 4, 9, 16, 25],
]
func largestNum(objDictionary:[String:Array<Int>]) -> (Int,String) {
var largest = 0
var ki:String? = nil
for (kind, numbers) in interestingNumbers {
for number in numbers {
if number > largest {
largest = number
ki=kind
}
}
}
return (largest , ki!)
}
print(largestNum(interestingNumbers)) //calling fuction and print
/*var ar2:[Int,String] = largestNum(interestingNumbers))
print(ar2)*/' this code have an error`
How can I store the returned values from the function in the array

Update:
If you want both values in a single array with ar[0] being the Int and ar[1] being the String, then you'll need to declare ar2 to be [Any] and unpack the tuple when initializing ar2:
let largest = largestNum(interestingNumbers)
var ar2:[Any] = [largest.0, largest.1]
print(ar2) // [80, "Fibonacci"]
If you just assign the return to ar2 and leave it as a tuple, you can access the values with ar2.0 and ar2.1:
var ar2 = largestNum(interestingNumbers)
print(ar2.0) // 80
print(ar2.1) // "Fibonacci"
Or if you change your largestNum to return a named tuple:
func largestNum(objDictionary:[String:Array<Int>]) -> (number: Int, kind: String) {
}
var ar2 = largestNum(interestingNumbers)
print(ar2.number) // 80
print(ar2.kind) // "Fibonacci"
Original Answer:
Declare your array ar2 to hold tuples pairs of Int and String, and then wrap your return value in [] to create an array:
var ar2:[(Int,String)] = [largestNum(interestingNumbers)]
print(ar2) // [(80, "Fibonacci")]
Because tuples are really meant for temporary values, it is better style to store values in an array using a struct:
Change your function to return an InterestingNumber:
struct InterestingNumber {
let kind: String
let number: Int
}
func largestNum(objDictionary:[String:Array<Int>]) -> InterestingNumber {
// contents omitted for brevity
return InterestingNumber(kind: ki!, number: largest)
}
let largest = largestNum(interestingNumbers)
// Define your array to hold `InterestingNumber`s:
var ar2:[InterestingNumber] = [largest]
print(ar2) // [InterestingNumber(kind: "Fibonacci", number: 80)]
If you meant for ar2 to just hold a single value, then simply do:
var ar2 = largestNum(interestingNumbers)
and Swift will infer the type (which is a tuple in your original code or an InterestingNumber when using the struct.

your code runs fine in xcode 7.3.1 playground
okay, now i get your question:
let z: (Int, String) = largestNum(interestingNumbers)
The part after the arrow in your function definition is the type (i think called tupel), you can use it for a variable.

Related

N-Dimensional array swift

Is there any way to have an n dimensional array in swift? I would like to be able to make a function that creates an array with n dimensions but I cannot figure out how.
Basically something like this:
func ndarray <T> (dimensions: Int...) -> [[T]] { // What do I tell it I return?
var out
for d in dimensions {
out = Array<T>(repeating: out, count: d)
}
return out
}
The above code does not work for obvios reasons but, I think it points out the main problems I am having:
How do I define a return type
How do I actually create the array
Once created how do I traverse and populate the array
Here is the implementation of an N-Dimensional Array. It uses a normal array internally for storage and converts the multi-dimensional indices into a single index for the internal array.
struct NDimArray<T> {
let dimensions: [Int]
var data: [T]
init(dimensions: Int..., initialValue: T) {
self.dimensions = dimensions
data = Array(repeating: initialValue, count: dimensions.reduce(1, *))
}
init(dimensions: Int..., initUsing initializer: () -> T) {
self.dimensions = dimensions
data = (0 ..< dimensions.reduce(1, *)).map { _ in initializer() }
}
// Compute index into data from indices
private func computeIndex(_ indices: [Int]) -> Int {
guard indices.count == dimensions.count else { fatalError("Wrong number of indices: got \(indices.count), expected \(dimensions.count)") }
zip(dimensions, indices).forEach { dim, idx in
guard (0 ..< dim) ~= idx else { fatalError("Index out of range") }
}
var idx = indices
var dims = dimensions
var product = 1
var total = idx.removeLast()
while !idx.isEmpty {
product *= dims.removeLast()
total += (idx.removeLast() * product)
}
return total
}
subscript(_ indices: Int...) -> T {
get {
return data[computeIndex(indices)]
}
set {
data[computeIndex(indices)] = newValue
}
}
}
Example:
// Create a 3 x 4 x 5 array of String with initial value ""
var arr = NDimArray<String>(dimensions: 3, 4, 5, initialValue: "")
for x in 0 ..< 3 {
for y in 0 ..< 4 {
for z in 0 ..< 5 {
// Encode indices in the string
arr[x, y, z] = "(\(x),\(y),\(z))"
}
}
}
// Show internal storage of data
print(arr.data)
["(0,0,0)", "(0,0,1)", "(0,0,2)", "(0,0,3)", "(0,0,4)", "(0,1,0)", "(0,1,1)", "(0,1,2)", "(0,1,3)", "(0,1,4)", "(0,2,0)", "(0,2,1)", "(0,2,2)", "(0,2,3)", "(0,2,4)", "(0,3,0)", "(0,3,1)", "(0,3,2)", "(0,3,3)", "(0,3,4)", "(1,0,0)", "(1,0,1)", "(1,0,2)", "(1,0,3)", "(1,0,4)", "(1,1,0)", "(1,1,1)", "(1,1,2)", "(1,1,3)", "(1,1,4)", "(1,2,0)", "(1,2,1)", "(1,2,2)", "(1,2,3)", "(1,2,4)", "(1,3,0)", "(1,3,1)", "(1,3,2)", "(1,3,3)", "(1,3,4)", "(2,0,0)", "(2,0,1)", "(2,0,2)", "(2,0,3)", "(2,0,4)", "(2,1,0)", "(2,1,1)", "(2,1,2)", "(2,1,3)", "(2,1,4)", "(2,2,0)", "(2,2,1)", "(2,2,2)", "(2,2,3)", "(2,2,4)", "(2,3,0)", "(2,3,1)", "(2,3,2)", "(2,3,3)", "(2,3,4)"]
print(arr[2, 2, 2]) // "(2,2,2)"
print(arr[3, 0, 0]) // Fatal error: Index out of range
print(arr[0, 4, 0]) // Fatal error: Index out of range
print(arr[2]) // Fatal error: Wrong number of indices: got 1, expected 3
Initializing an Array with a Reference Type
As #DuncanC noted in the comments, you have to be careful when initializing an array with a value which is a reference type, because the array will be filled with references to the object and modifying the object at any index will modify all of them.
To solve this, I added a second initializer:
init(dimensions: Int..., initUsing initializer: () -> T)
which takes a closure () -> T which can be used to create a new object for each element of the array.
For example:
class Person {
var name = ""
}
// Pass a closure which creates a `Person` instance to fill the array
// with 25 person objects
let arr = NDimArray(dimensions: 5, 5, initUsing: { Person() })
arr[3, 3].name = "Fred"
arr[2, 2].name = "Wilma"
print(arr[3, 3].name, arr[2, 2].name)
Fred Wilma
Nope, it's not possible. Array dimensions is something that needs to be determined at compile time, while the argument you want to pass to the initializer will not be known until runtime. If you really want to achieve something like this, then you'll need to move the array indexing from compile time to runtime, e.g. by accessing the array via an array of indexes. Still you don't have compile validation, since the array length can at runtime to not match the dimensions of the array.
This problem is similar to the one that attempts to convert a tuple to an array.

Averaging elements in array of arrays by index using functional programming

I have an array of arrays of Doubles. For example:
let mceGain = [[3,4,5],[7,4,3],[12,10,7]] // Written as integers for simplicity here
I would now like to average the elements in the different arrays with corresponding indexes. So I would have an output looking somewhat like this:
//firstAvg: (3+7+12)/3 = 7.33
//secondAvg: (4+4+10)/3 = 6
//thirdAvg: (5+3+7)/3 = 5
Then finally I would like to store these averages in a simpler array:
//mceGain: [7.33,6,5]
I have tried to do this with a double for-loop with a switch-statement inside, but this seems to be unnecessarily complicated. I assume the same result could be achieved using a combination of reduce(), map() and filter(), but I cannot seem to wrap my head around it.
Let's analyse what you want to do here. You start with an array of arrays:
[[3,4,5],[7,4,3],[12,10,7]]
and you want to transform each subarray into a number:
[7,6,5]
Whenever you have this kind of "transform each element of this sequence into something else" situation, use map.
When you compute the average, you need to transform a sequence of things into just one thing. This means that we need reduce.
let array: [[Double]] = [[3,4,5],[7,4,3],[12,10,7]]
let result = array.map { $0.reduce(0.0, { $0 + $1 }) / Double($0.count) }
With comments:
let array: [[Double]] = [[3,4,5],[7,4,3],[12,10,7]]
let result = array.map { // transform each element like this:
$0.reduce(0.0, { $0 + $1 }) // sums everything in the sub array up
/ Double($0.count) } // divide by count
EDIT:
What you need to do is to "transpose" the array first, then do the map and reduce:
array[0].indices.map{ index in // these three lines makes the array [[3, 7, 12], [4, 4, 10], [5, 3, 7]]
array.map{ $0[index] }
}
.map { $0.reduce(0.0, { $0 + $1 }) / Double($0.count) }
This should answer your comment below
let elms: [[Double]] = [[3, 5, 3], [4, 4, 10] , [5, 3, 7]]
func averageByIndex(elms:[[Double]]) -> [Double]? {
guard let length = elms.first?.count else { return []}
// check all the elements have the same length, otherwise returns nil
guard !elms.contains(where:{ $0.count != length }) else { return nil }
return (0..<length).map { index in
let sum = elms.map { $0[index] }.reduce(0, +)
return sum / Double(elms.count)
}
}
if let averages = averageByIndex(elms: elms) {
print(averages) // [4.0, 4.0, 6.666666666666667]
}

Transforming RandomAccessSlice to RandomAccessCollection

When we try to retrieve a range of elements from an Array, we get back an ArraySlice:
let array = [1, 3, 5, 2]
let arraySlice = array[..<2] // elements up to index 1 == [1, 3]
We can transform it back to the Array type like so:
let arrayFromSlice = Array(arraySlice)
Let's say you want to create a method that returns the first 3 elements of any RandomAccessCollection:
func first3Elements<T: RandomAccessCollection>(_ c: T) -> T {
let slice = c.prefix(3)
// COMPILER ERROR: non-nominal type 'T'
// does not support explicit initialization
return T(slice)
}
Is it possible to perform this conversion?
Here my first attempt using type erasure but I guess there are better solutions.
func first3Elements<T>(_ c: AnyRandomAccessCollection<T>) -> AnyRandomAccessCollection<T> {
let slice = c.prefix(3)
return AnyRandomAccessCollection(slice)
}
let array = AnyRandomAccessCollection([1, 2, 3, 4])
let result = first3Elements(array)
for x in result {
print(x)
}

Easy way to check an array for contiguous three or more numbers in Swift

Is there any easy way to check if an array contains contiguous value of three or more? e.g. [4, 2, 1, 1, 1, 7, 4, 4, 4, 4] contains two contiguous sequence of 1 and 4. To check I wish to give 1 and minimum allowed conjugation, in this case 2, it will just return true. Thanks.
We can start out by making use of a neat extension to SequenceType by user #oisdk in his answer in the following thread:
How to find same value(duplicate) in an int array in order?
The extension groups successive elements in tuples (value, numberOfSuccessions):
/* from SO user #oisdk:s answer in Q&A:
https://stackoverflow.com/a/35325141/4573247 */
extension SequenceType where Generator.Element: Equatable {
func group() -> [(Generator.Element, Int)] {
var res: [(Generator.Element, Int)] = []
for el in self {
if res.last?.0 == el {
res[res.endIndex-1].1 += 1
} else {
res.append((el,1))
}
}
return res
}
}
Using this, we can swiftly write another extension for checking if---for a given array---a contiguous sequence (for some minimum number of successions/repeats) exists for a given number:
extension SequenceType where Generator.Element == Int {
func containsContiguousValue(value: Int, forMinimumRepeats rep: Int) -> Bool {
return !self
.group()
.contains{ (val, count) in count >= rep && val == value }
}
}
Used as follows
/* Example usage */
let array = [4, 2, 1, 1, 1, 7, 4, 4, 4, 4]
array.containsContiguousValue(1, forMinimumRepeats: 3) // true
array.containsContiguousValue(1, forMinimumRepeats: 4) // false
array.containsContiguousValue(4, forMinimumRepeats: 4) // true
array.containsContiguousValue(2, forMinimumRepeats: 3) // false
I think the simplest possible way is with the help of the reduce function. If you want you can extend the data structures, but I am not quite a fan of that. So here is a simple solution to your example
// example array
let a = [4, 2, 1, 1, 1, 7, 4, 4, 4, 4]
let minRepeats = 3 // desired min repeats
let elementToCheck = 4 // element to check
let m = a.reduce(0) { (initial: Int, el: Int) -> Int in
if initial >= minRepeats {
return initial
} else {
return el == elementToCheck ? initial + 1 : 0
}
}
// if m == minRepeats the check is positive, if m < minRepeats the check is negative
// let check = a.reduce(0){...} == minRepeats gives you the right result
// Thanks to user3441734 for the correction
The answers above were helpful but not quite as generic as I needed. Also, they are a little outdated, so for those who come across this requirement, here's a generic reusable Swift 4.2 implementation:
An extension on any Collection that returns an array of ranges representing the indices of consecutive elements in a collection matching a given predicate.
https://gist.github.com/shaps80/8ec24f82ad1e54d42709277ec2af93a3

How to convert a String (numeric) in a Int array in Swift

I'd like to know how can I convert a String in an Int array in Swift.
In Java I've always done it like this:
String myString = "123456789";
int[] myArray = new int[myString.lenght()];
for(int i=0;i<myArray.lenght;i++){
myArray[i] = Integer.parseInt(myString.charAt(i));
}
Thanks everyone for helping!
let str = "123456789"
let intArray = map(str) { String($0).toInt() ?? 0 }
map() iterates Characters in str
String($0) converts Character to String
.toInt() converts String to Int. If failed(??), use 0.
If you prefer for loop, try:
let str = "123456789"
var intArray: [Int] = []
for chr in str {
intArray.append(String(chr).toInt() ?? 0)
}
OR, if you want to iterate indices of the String:
let str = "123456789"
var intArray: [Int] = []
for i in indices(str) {
intArray.append(String(str[i]).toInt() ?? 0)
}
You can use flatMap to convert the characters into a string and coerce the character strings into an integer:
Swift 2 or 3
let string = "123456789"
let digits = string.characters.flatMap{Int(String($0))}
print(digits) // [1, 2, 3, 4, 5, 6, 7, 8, 9]"
Swift 4
let string = "123456789"
let digits = string.flatMap{Int(String($0))}
print(digits) // [1, 2, 3, 4, 5, 6, 7, 8, 9]"
Swift 4.1
let digits = string.compactMap{Int(String($0))}
Swift 5 or later
We can use the new Character Property wholeNumberValue https://developer.apple.com/documentation/swift/character/3127025-wholenumbervalue
let digits = string.compactMap{$0.wholeNumberValue}
#rintaro's answer is correct, but I just wanted to add that you can use reduce to weed out any characters that can't be converted to an Int, and even display a warning message if that happens:
let str = "123456789"
let intArray = reduce(str, [Int]()) { (var array: [Int], char: Character) -> [Int] in
if let i = String(char).toInt() {
array.append(i)
} else {
println("Warning: could not convert character \(char) to an integer")
}
return array
}
The advantages are:
if intArray contains zeros you will know that there was a 0 in str, and not some other character that turned into a zero
you will get told if there is a non-Int character that is possibly screwing things up.
Swift 3
Int array to String
let arjun = [1,32,45,5]
print(self.get_numbers(array: arjun))
func get_numbers(array:[Int]) -> String {
let stringArray = array.flatMap { String(describing: $0) }
return stringArray.joined(separator: ",")
String to Int Array
let arjun = "1,32,45,5"
print(self.get_numbers(stringtext: arjun))
func get_numbers(stringtext:String) -> [Int] {
let StringRecordedArr = stringtext.components(separatedBy: ",")
return StringRecordedArr.map { Int($0)!}
}
var myString = "123456789"
var myArray:[Int] = []
for index in 0..<countElements(myString) {
var myChar = myString[advance(myString.startIndex, index)]
myArray.append(String(myChar).toInt()!)
}
println(myArray) // [1, 2, 3, 4, 5, 6, 7, 8, 9]"
To get the iterator pointing to a char from the string you can use advance
The method to convert string to int in Swift is toInt()
Swift 3 update:
#appzYourLife : That's correct toInt() method is no longer available for String in Swift 3.
As an alternative what you can do is :
intArray.append(Int(String(chr)) ?? 0)
Enclosing it within Int() converts it to Int.
Swift 3: Functional Approach
Split the String into separate String instances using:
components(separatedBy separator: String) -> [String]
Reference: Returns an array containing substrings from the String that have been divided by a given separator.
Use the flatMap Array method to bypass the nil coalescing while converting to Int
Reference: Returns an array containing the non-nil results of calling the given transformation with each element of this sequence.
Implementation
let string = "123456789"
let intArray = string.components(separatedBy: "").flatMap { Int($0) }
let array = "0123456789".compactMap{ Int(String($0)) }
print(array)

Resources