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.
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.
I have done this in Objective-C but I can't do what I want to do in Swift.
I am trying to rotate a 2 dimensional array of any Type. I am using generics so that I could Strings & Ints or any other type.
import UIKit
let someArray = [[1,2,3],[7,8,9],[11,93,87]]
print(someArray[0])
func rotateArray<T> (array:[[T]]) ->Array{
var tempArray = [[T]]()
for i in 0..<array.count{
for j in 0..<array.count{
tempArray[j][array.count-i-1] = array[i][j]
}
}
return tempArray
}
someArray.count
let x = rotateArray(someArray)
However I get the following errors ( There could be other errors that I am not aware of), I also read this question and some others but couldn't relate to it.
reference to generic type 'Array' requires arguments in <..>
Binary Operator '..<' Cannot be applied to two 'Int' operands
Edit after fixing the initial two errors: fatal error: Index out of range
What are the things that I am doing wrong? Kindly include details, I am a complete noob.
You have written the return type -> Array, but since Array is generic, you need to specify what it contains, such as Array<Something> or equivalently [Something].
Seemingly, you want to return the same "shape"/type of array as the input, so you can use -> [[T]] for your return type.
(I'm not sure why the compiler produced an error about ..<, but it goes away if you fix the first issue.)
In addition to making your method return type [[T]], you have other problems here. You are instantiating tempArray (the array that will hold the arrays inside), but you are not instantiating those inner arrays. And you can't just use subscript operator, but rather you have to append to your respective arrays.
For example, if you want to rotate clockwise 90 degrees, it would be:
func rotateArray<T> (array:[[T]]) -> [[T]] {
var tempArray = [[T]]()
for column in 0 ..< array.first!.count {
var rowArray = [T]()
for row in (0 ..< array.count).reverse() {
rowArray.append(array[row][column])
}
tempArray.append(rowArray)
}
return tempArray
}
Thus
[[1, 2, 3], [7, 8, 9], [11, 93, 87]]
Becomes
[[11, 7, 1], [93, 8, 2], [87, 9, 3]]
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
I want to improve on a closure I wrote using Swift's Array.map function
I'm basically taking an Array and remapping all of its elements using a closure.
// Here's the array:
var numbersArray = [1, 2, 3, 4, 5]
// Here's the closure - it takes whatever number is passed into it and
// multiplies it by 2.0
var multiplier = { (currentNum: Int) -> Double in
let result = Double(currentNum) * 2.0
return result
}
// And here's how I'm calling it:
var newArray = numbersArray.map(multiplier)
And this works perfectly.
But what if I want to multiply everything by 2.1? or 3.5? or any value? In other words, what if I want to make the amount I multiply by also be a variable? And have it be passed into the closure as a second argument?
I tried adding it to the argument list like this:
var multiplier = { (currentNum: Int, factor: Double) -> Double in
let result = Double(currentNum) * factor
return result
}
and then changing my call to this:
var newArray = numbersArray.map(multiplier, 3.5)
but I'm getting all sorts of errors (and I tried all sorts of variations on this of course.)
What am I doing wrong?
Edit: Note: This language feature was removed in Swift 2.
A swift-er way than connor's answer (but along the same lines), is to use a curried function. From The Swift Programming Language->Language Reference->Declarations->Curried Functions and Methods:
A function declared this way is understood as a function whose return
type is another function.
So you can simplify this:
func multiplier(factor: Double) -> (Int)->Double
{
return { (currentNum: Int) -> Double in
let result = Double(currentNum) * factor
return result
}
}
to this:
func multiplier(factor: Double)(currentNum: Int) -> Double {
return Double(currentNum) * factor
}
and use it exactly the same way:
let numbersArray = [1, 2, 3, 4, 5]
let multipliedArray = numbersArray.map(multiplier(3.5))
You can use a higher order function to produce a custom function that you can then use with the array's map function. Like this:
var numbersArray = [1, 2, 3, 4, 5]
func multiplier(factor: Double) -> (Int)->Double
{
return { (currentNum: Int) -> Double in
let result = Double(currentNum) * factor
return result
}
}
var newArray = numbersArray.map(multiplier(2.5))