How do I add a tuple to a Swift Array? - arrays

I'm trying to add a tuple (e.g., 2-item tuple) to an array.
var myStringArray: (String,Int)[]? = nil
myStringArray += ("One", 1)
What I'm getting is:
Could not find an overload for '+=' that accepts the supplied
arguments
Hint: I tried to do an overload of the '+=' per reference book:
#assignment func += (inout left: (String,Int)[], right: (String,Int)[]) {
left = (left:String+right:String, left:Int+right+Int)
}
...but haven't got it right.
Any ideas? ...solution?

Since this is still the top answer on google for adding tuples to an array, its worth noting that things have changed slightly in the latest release. namely:
when declaring/instantiating arrays; the type is now nested within the braces:
var stuff:[(name: String, value: Int)] = []
the compound assignment operator, +=, is now used for concatenating arrays; if adding a single item, it needs to be nested in an array:
stuff += [(name: "test 1", value: 1)]
it also worth noting that when using append() on an array containing named tuples, you can provide each property of the tuple you're adding as an argument to append():
stuff.append((name: "test 2", value: 2))

You have two issues. First problem, you're not creating an "array of tuples", you're creating an "optional array of tuples". To fix that, change this line:
var myStringArray: (String,Int)[]? = nil
to:
var myStringArray: (String,Int)[]
Second, you're creating a variable, but not giving it a value. You have to create a new array and assign it to the variable. To fix that, add this line after the first one:
myStringArray = []
...or you can just change the first line to this:
var myStringArray: (String,Int)[] = []
After that, this line works fine and you don't have to worry about overloading operators or other craziness. You're done!
myStringArray += ("One", 1)
Here's the complete solution. A whopping two lines and one wasn't even changed:
var myStringArray: (String,Int)[] = []
myStringArray += ("One", 1)

Swift 4 solution:
// init empty tuple array
var myTupleArray: [(String, Int)] = []
// append a value
myTupleArray.append(("One", 1))

If you remove the optional, it works fine, otherwise you'll have to do this:
var myStringArray: (String,Int)[]? = nil
if !myStringArray {
myStringArray = []
}
var array = myStringArray!
array += ("One", 1)
myStringArray = array
You can never append an empty array, so you'll have to initialize it at some point. You'll see in the overload operator below that we sort of lazy load it to make sure that it is never nil.
You could condense this into a '+=' operator:
#assignment func += (inout left: Array<(String, Int)>?, right: (String, Int)) {
if !left {
left = []
}
var array = left!
array.append(right.0, right.1)
left = array
}
Then call:
var myStringArray: (String,Int)[]? = nil
myStringArray += ("one", 1)

I've ended up here multiple times due to this issue. Still not as easy as i'd like with appending onto an array of tuples. Here is an example of how I do it now.
Set an alias for the Tuple - key point
typealias RegionDetail = (regionName:String, constraintDetails:[String:String]?)
Empty array
var allRegionDetails = [RegionDetail]()
Easy to add now
var newRegion = RegionDetail(newRegionName, constraints)
allRegionDetails.append(newRegion)
var anotherNewRegion = RegionDetail("Empty Thing", nil)
allRegionDetails.append(anotherNewRegion)

Note:
It's not work anymore
if you do:
array += tuple
you will get error
what you need is :
array += [tuple]
I think apple change to this representation because it's more logical

#assignment func += (inout left: Array<(String, Int)>?, right: (String, Int)) {
if !left {
left = []
}
if left {
var array = left!
array.append(right.0, right.1)
left = array
}
}
var myStringArray: (String,Int)[]? = nil
myStringArray += ("x",1)

Thanks to comments:
import UIKit
#assignment func += (inout left: Array<(String, Int)>?, right: (String, Int)) {
if !left {
left = []
}
if left {
var array = left!
array.append(right.0, right.1)
left = array
}
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let interestingNumbers = [
"Prime": [2, 3, 5, 7, 11, 13],
"Fibonacci": [1, 1, 2, 3, 5, 8],
"Square": [1, 4, 9, 16, 25],
]
println("interestingNumbers: \(interestingNumbers)\n")
var largest = 0
var myStringArray: (String,Int)[]? = nil
myStringArray += ("One", 1)
var x = 0
for (kind, numbers) in interestingNumbers {
println(kind)
for number in numbers {
if number > largest {
largest = number
}
x++
println("\(x)) Number: \(number)")
myStringArray += (kind,number)
} // end Number
} // end Kind
println("myStringArray: \(myStringArray)")
}
}
The Output:
interestingNumbers: [Square: [1, 4, 9, 16, 25], Prime: [2, 3, 5, 7,
11, 13], Fibonacci: [1, 1, 2, 3, 5, 8]]
Square 1) Number: 1 2) Number: 4 3) Number: 9 4)
Number: 16 5) Number: 25 Prime 6) Number: 2 7)
Number: 3 8) Number: 5 9) Number: 7 10) Number: 11
11) Number: 13 Fibonacci 12) Number: 1 13) Number:
1 14) Number: 2 15) Number: 3 16) Number: 5 17)
Number: 8
Array of tupules:
myStringArray: [(One, 1), (Square, 1), (Square, 4), (Square, 9),
(Square, 16), (Square, 25), (Prime, 2), (Prime, 3), (Prime, 5),
(Prime, 7), (Prime, 11), (Prime, 13), (Fibonacci, 1), (Fibonacci, 1),
(Fibonacci, 2), (Fibonacci, 3), (Fibonacci, 5), (Fibonacci, 8)]

Related

How to divide all items in an array to a Double in Swift

So if I have
var passengersInDay: [Int] = [3, 3, 3, 3, 3, 2, 2, 1, 1, 1]
var tripCost:Double
var tripDuration:Int
tripCostPerDay = tripCost / Double(tripDuration)
How to divide all elements of the passengersInDay array to the tripCostPerDay and put the result into a new array consisting of Doubles?
I tried using this operation
var singleDayCosts:[Double] = tripCostPerDay / passengersInDay
But swift keeps telling me that I can't divide an array that is expecting an int into a Double?, so I tried to call it Double(passengersInDay) but then a different error showed up.
Any thoughts?
Try
let singleDayCosts = passengersInDay.map { tripCostPerDay / Double($0) }

Pairing last element from previous pair as first in next pair [duplicate]

Given I've got an array in Swift such as [1,2,3,4], a method pairs() will transform it in to the array of tuples: [(1,2), (2,3), (3,4)].
Here are some more examples of how pairs() should behave:
pairs([]) should return [] as it has no pairs.
pairs([1]) should also return [], as it has no pairs.
pairs([1,2]) should be [(1,2)]. It has just one pair.
I can write code to do this for Array, but I'd like to have pairs() available as an extension on Sequence, so that it returns a Sequence of the pairs. This would make it useable on any sequence, and compatible with methods such as map, reduce, filter, etc.
How do I go about creating a Sequence like this? And how do I write the method to transform any Sequence in this way so that it can be used as flexibly as possible?
We can use zip() and dropFirst() if we define an extension
on the Collection type:
extension Collection {
func pairs() -> AnySequence<(Element, Element)> {
return AnySequence(zip(self, self.dropFirst()))
}
}
Example:
let array = [1, 2, 3, 4]
for p in array.pairs() {
print(p)
}
Output:
(1, 2)
(2, 3)
(3, 4)
More examples:
print(Array("abc".pairs()))
// [("a", "b"), ("b", "c")]
print([1, 2, 3, 4, 5].pairs().map(+))
// [3, 5, 7, 9]
print([3, 1, 4, 1, 5, 9, 2].pairs().filter(<))
// [(1, 4), (1, 5), (5, 9)]
(Unlike I wrote in the first version of this answer ...) this
approach is not safe when applied to a Sequence, because it is
not guaranteed that a sequence can be traversed multiple times
non-destructively.
Here is a direct implementation with a custom iterator type
which works on sequences as well:
struct PairSequence<S: Sequence>: IteratorProtocol, Sequence {
var it: S.Iterator
var last: S.Element?
init(seq: S) {
it = seq.makeIterator()
last = it.next()
}
mutating func next() -> (S.Element, S.Element)? {
guard let a = last, let b = it.next() else { return nil }
last = b
return (a, b)
}
}
extension Sequence {
func pairs() -> PairSequence<Self> {
return PairSequence(seq: self)
}
}
Example:
print(Array([1, 2, 3, 4].pairs().pairs()))
// [((1, 2), (2, 3)), ((2, 3), (3, 4))]

Rotate kotlin array

Assume that I have an array like 1 2 3 4 5, I want to rotate it to the left by n and get a new one.
For example the 2 rotation of the above array will result in 3 4 5 1 2. I didn't found any extension function to do that.
You can use built-in java Collections.rotate method, but you need to convert your array to list firstly:
val arr = intArrayOf(1, 2, 3, 4, 5)
val list = arr.toList()
Collections.rotate(list, -2)
println(list.toIntArray().joinToString())
Outputs
3, 4, 5, 1, 2
I interpret "get a new one" to mean that the extension function should return a new array instance, like so (boundary checks omitted, sliceArray is an stdlib function) :
fun <T> Array<T>.rotate(n: Int) =
let { sliceArray(n until size) + sliceArray(0 until n) }
Example
arrayOf(1, 2, 3, 4, 5).rotate(1)
.also { println(it.joinToString()) } // 2, 3, 4, 5, 1
Another extension function, by slicing the array in 2 parts left and right and reassembling it to right + left:
fun <T> Array<T>.leftShift(d: Int) {
val n = d % this.size // just in case
if (n == 0) return // no need to shift
val left = this.copyOfRange(0, n)
val right = this.copyOfRange(n, this.size)
System.arraycopy(right, 0, this, 0, right.size)
System.arraycopy(left, 0, this, right.size, left.size)
}
so this:
val a = arrayOf(1, 2, 3, 4, 5, 6, 7)
a.leftShift(2)
a.forEach { print(" " + it) }
will print
3 4 5 6 7 1 2
Simple solution:
fun <T> Array<T>.rotateLeft(n: Int) = drop(n) + take(n)
fun <T> Array<T>.rotateRight(n: Int) = takeLast(n) + dropLast(n)
The limitation is that n must be less than or equal to the length of the array.
Alternatively, you can use Collections.rotate(...) as follows.
import java.util.Collections
fun <T> Array<T>.rotate(distance: Int) =
toList().also { // toList() is a deep copy to avoid changing the original array.
Collections.rotate(it, distance)
}
fun main() {
val xs = arrayOf(1, 2, 3, 4, 5)
val ys = xs.rotate(-2)
xs.forEach { print("$it ") } // 1 2 3 4 5
println(ys) // [3, 4, 5, 1, 2]
}
For the record, you can use the regular Array constructor to build a new array:
inline fun <reified T> Array<T>.rotate(n: Int) = Array(size) { this[(it + n) % size] }
The element at index it in the source array is copied in the destination array at the new index (it + n) % size to perform the rotation.
It is a bit slower than copying the array by chunks.

Swift Comparing Elements of Two Separate Arrays

I am trying to compare the elements in two different arrays in Swift without using higher order functions. The function should return an array of integers that are in both arrays. I think I am close, but am getting 'an index out range error. Also would like to know how this measures on time complexity
let arrayOne = [1, 5, 12, 3, -15 , 52, 20]
let arrayTwo = [3, 1, 6, 5, 57, 13, 17, 20]
func compareElementsInArray(array1:[Int], array2: [Int]) -> [Int] {
let totalArray = array1 + array2
var sortedArray = totalArray.sorted()
var results = [Int]()
for i in totalArray {
if sortedArray[i + 1] == sortedArray[i] {
results.append(sortedArray[i])
}
}
return results
}
compareElementsInArray(array1: arrayOne, array2: arrayTwo)
The problem is that you are iterating through all element of totalArray meaning that i will reach the last index of totalArray, then you are trying to access the i+1-th element of sortedArray, which has the same length as totalArray, hence the error.
You need to stop the loop at the index before the last one, not the last one.
func compareElementsInArray(array1:[Int], array2: [Int]) -> [Int] {
let totalArray = array1 + array2
var sortedArray = totalArray.sorted()
var results = [Int]()
for i in 0..<totalArray.count-1 {
if sortedArray[i + 1] == sortedArray[i] {
results.append(sortedArray[i])
}
}
return results
}
print(compareElementsInArray(array1: arrayOne, array2: arrayTwo))
However, you can use an NSCountedSet to achieve the same using higher order functions (your solutions doesn't actually use higher order functions).
You just have to create a counted set from the combination of the arrays, then use flatMap to filter the elements whose count is greater than 1 and map the result to [Int].
func nonUniqueElements(array1: [Int], array2: [Int])->[Int] {
let countedSet = NSCountedSet(array: array1+array2)
return countedSet.flatMap({ element in
if countedSet.count(for: element) > 1 {
return element as? Int
} else {
return nil
}
})
}
nonUniqueElements(array1: arrayOne, array2: arrayTwo)

Find min / max value in Swift Array

Given an array of Swift numeric values, how can I find the minimum and maximum values?
I've so far got a simple (but potentially expensive) way:
var myMax = sort(myArray,>)[0]
And how I was taught to do it at school:
var myMax = 0
for i in 0..myArray.count {
if (myArray[i] > myMax){myMax = myArray[i]}
}
Is there a better way to get the minimum or maximum value from an integer Array in Swift? Ideally something that's one line such as Ruby's .min and .max.
Given:
let numbers = [1, 2, 3, 4, 5]
Swift 3:
numbers.min() // equals 1
numbers.max() // equals 5
Swift 2:
numbers.minElement() // equals 1
numbers.maxElement() // equals 5
To calculate an array's min and max values yourself, you can use reduce. This was a key solution prior to .min() and .max() appearing in Swift.
Use the almighty reduce:
let nums = [1, 6, 3, 9, 4, 6];
let numMax = nums.reduce(Int.min, { max($0, $1) })
Similarly:
let numMin = nums.reduce(Int.max, { min($0, $1) })
reduce takes a first value that is the initial value for an internal accumulator variable, then applies the passed function (here, it's anonymous) to the accumulator and each element of the array successively, and stores the new value in the accumulator. The last accumulator value is then returned.
With Swift 5, Array, like other Sequence Protocol conforming objects (Dictionary, Set, etc), has two methods called max() and max(by:) that return the maximum element in the sequence or nil if the sequence is empty.
#1. Using Array's max() method
If the element type inside your sequence conforms to Comparable protocol (may it be String, Float, Character or one of your custom class or struct), you will be able to use max() that has the following declaration:
#warn_unqualified_access func max() -> Element?
Returns the maximum element in the sequence.
The following Playground codes show to use max():
let intMax = [12, 15, 6].max()
let stringMax = ["bike", "car", "boat"].max()
print(String(describing: intMax)) // prints: Optional(15)
print(String(describing: stringMax)) // prints: Optional("car")
class Route: Comparable, CustomStringConvertible {
let distance: Int
var description: String { return "Route with distance: \(distance)" }
init(distance: Int) {
self.distance = distance
}
static func ==(lhs: Route, rhs: Route) -> Bool {
return lhs.distance == rhs.distance
}
static func <(lhs: Route, rhs: Route) -> Bool {
return lhs.distance < rhs.distance
}
}
let routes = [
Route(distance: 20),
Route(distance: 30),
Route(distance: 10)
]
let maxRoute = routes.max()
print(String(describing: maxRoute)) // prints: Optional(Route with distance: 30)
#2. Using Array's max(by:) method
If the element type inside your sequence does not conform to Comparable protocol, you will have to use max(by:) that has the following declaration:
#warn_unqualified_access func max(by areInIncreasingOrder: (Element, Element) throws -> Bool) rethrows -> Element?
Returns the maximum element in the sequence, using the given predicate as the comparison between elements.
The following Playground codes show to use max(by:):
let dictionary = ["Boat" : 15, "Car" : 20, "Bike" : 40]
let keyMaxElement = dictionary.max(by: { (a, b) -> Bool in
return a.key < b.key
})
let valueMaxElement = dictionary.max(by: { (a, b) -> Bool in
return a.value < b.value
})
print(String(describing: keyMaxElement)) // prints: Optional(("Car", 20))
print(String(describing: valueMaxElement)) // prints: Optional(("Bike", 40))
class Route: CustomStringConvertible {
let distance: Int
var description: String { return "Route with distance: \(distance)" }
init(distance: Int) {
self.distance = distance
}
}
let routes = [
Route(distance: 20),
Route(distance: 30),
Route(distance: 10)
]
let maxRoute = routes.max(by: { (a, b) -> Bool in
return a.distance < b.distance
})
print(String(describing: maxRoute)) // prints: Optional(Route with distance: 30)
The other answers are all correct, but don't forget you could also use collection operators, as follows:
var list = [1, 2, 3, 4]
var max: Int = (list as AnyObject).valueForKeyPath("#max.self") as Int
you can also find the average in the same way:
var avg: Double = (list as AnyObject).valueForKeyPath("#avg.self") as Double
This syntax might be less clear than some of the other solutions, but it's interesting to see that -valueForKeyPath: can still be used :)
You can use with reduce:
let randomNumbers = [4, 7, 1, 9, 6, 5, 6, 9]
let maxNumber = randomNumbers.reduce(randomNumbers[0]) { $0 > $1 ? $0 : $1 } //result is 9
Swift 3.0
You can try this code programmatically.
func getSmallAndGreatestNumber() -> Void {
let numbers = [145, 206, 116, 809, 540, 176]
var i = 0
var largest = numbers[0]
var small = numbers[0]
while i < numbers.count{
if (numbers[i] > largest) {
largest = numbers[i]
}
if (numbers[i] < small) {
small = numbers[i]
}
i = i + 1
}
print("Maximum Number ====================\(largest)")// 809
print("Minimum Number ====================\(small)")// 116
}
With Swift 1.2 (and maybe earlier) you now need to use:
let nums = [1, 6, 3, 9, 4, 6];
let numMax = nums.reduce(Int.min, combine: { max($0, $1) })
For working with Double values I used something like this:
let nums = [1.3, 6.2, 3.6, 9.7, 4.9, 6.3];
let numMax = nums.reduce(-Double.infinity, combine: { max($0, $1) })
In Swift 2.0, the minElement and maxElement become methods of SequenceType protocol, you should call them like:
let a = [1, 2, 3]
print(a.maxElement()) //3
print(a.minElement()) //1
Using maxElement as a function like maxElement(a) is unavailable now.
The syntax of Swift is in flux, so I can just confirm this in Xcode version7 beta6.
It may be modified in the future, so I suggest that you'd better check the doc before you use these methods.
Here's a performance test for the solutions posted here. https://github.com/tedgonzalez/MaxElementInCollectionPerformance
This is the fastest for Swift 5
array.max()
var numbers = [1, 2, 7, 5];
var val = sort(numbers){$0 > $1}[0];
Apple's Swift Algorithms introduced 2021 contains Minima and/or Maxima which is likely highly optimized.
Example from the documentation:
let numbers = [7, 1, 6, 2, 8, 3, 9]
if let (smallest, largest) = numbers.minAndMax(by: <) {
// Work with 1 and 9....
}
The total complexity is O(k log k + nk), which will result in a runtime close to O(n) if k is a small amount. If k is a large amount (more than 10% of the collection), we fall back to sorting the entire array. Realistically, this means the worst case is actually O(n log n).
Updated for Swift 3/4:
Use below simple lines of code to find the max from array;
var num = [11, 2, 7, 5, 21]
var result = num.sorted(){
$0 > $1
}
print("max from result: \(result[0])") // 21
Just curious why you think the way it was taught in school is potentially expensive? You're running a for loop which has the time complexity of O(N). That's actually better than most sorting algorithms and equivalent to the performance of higher-order methods like reduce.
So I would think that in terms of performance, a for loop is as good as it gets. I don't think you'll find anything better than O(N) for finding max.
Of course, just use the .max() method provided by apple is the way to go.
If both minimum and maximum values are desired, an efficient approach could be to use a single reduce operation with a tuple:
let values = [11, 2, 7, 5, 21]
let (minimum, maximum) = values.reduce((Int.max, Int.min)) {
(min($0.0, $1), max($0.1, $1))
}
print(minimum, maximum) // 2, 21
let array: [Int] = [2, -22, -1, -5600, 333, -167]
var min = 0
var max = array[0]
for i in array {
// finding the min
if min > i {
min = i
}
// finding the max
if max < i {
max = i
}
}
print("Minimum: \(min)\nMaximum: \(max)")
You can also sort your array and then use array.first or array.last

Resources