How to transpose an array more Swiftly? - swift

I want to transpose an array of arrays like this:
Original array:
[
[1,2,3],
[4,5,6],
[7,8,9]
]
Result:
[
1,4,7,
2,5,8,
3,6,9
]
[1,4,7,2,5,8,3,6,9]
Assume all the subarrays have the same length.
If you haven't noticed already, the first three items in the result is the first item of the three subarrays. The fourth, fifth and sixth items in the result is the second item of each subarray.
If you still don't understand, maybe this will help:
At the moment, I have this:
func flatten(array: [[Int]]) -> [Int] {
var flat = [Int]()
for i in 0..<array[0].count {
for subarray in array {
flat.append(subarray[i])
}
}
return flat
}
I don't think that is very swfity. How can I do this in a swifty way?
Just to avoid being an XY problem, here's why I want to do this.
I am developing a board game. I am using HLGridNode (It's basically a bunch of squares in a grid-like layout) from HLSpriteKit as the board game's board. To edit the contents of the grid node, I need to pass in an 1D array of sprite nodes, not a 2D array.
To make my life easier, I stored the model objects in a 2D array. This way, I can refer to the sqaure 5 squares from the left and 2 squares from the top just by doing:
modelObjects[5][2]
If I flatten the 2D array using .flatMap { $0 } and pass the result to the grid node, modelObjects[5][2] would appear to be 2 squares from the left and 5 squares from the top.
This is not a duplicate of this because that question seems to have definite number of arrays to work with. Although I can put my 2D array into a loop, and do those enumerate().map {...} stuff, it seems like a really long-winded approach. I think there must be a simpler to do this with 2D arrays.

Here's an improvement on Shadow Of's answer:
extension Collection where Self.Iterator.Element: RandomAccessCollection {
// PRECONDITION: `self` must be rectangular, i.e. every row has equal size.
func transposed() -> [[Self.Iterator.Element.Iterator.Element]] {
guard let firstRow = self.first else { return [] }
return firstRow.indices.map { index in
self.map{ $0[index] }
}
}
}
let matrix = [
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
]
matrix.transposed().forEach{ print($0) }

You can receive result you wanted by transpose your 2d matrix, using, for example, this function:
func matrixTranspose<T>(_ matrix: [[T]]) -> [[T]] {
if matrix.isEmpty {return matrix}
var result = [[T]]()
for index in 0..<matrix.first!.count {
result.append(matrix.map{$0[index]})
}
return result
}
and applying flatten (joined in swift 3) then.
let arr = [[1,2,3],[4,5,6],[7,8,9]]
print(matrixTranspose(arr))
// [[1, 4, 7], [2, 5, 8], [3, 6, 9]]
print(matrixTranspose(arr).flatMap{$0})
// [1, 4, 7, 2, 5, 8, 3, 6, 9]
Extension version:
extension Collection where Self.Iterator.Element: Collection {
var transpose: Array<Array<Self.Iterator.Element.Iterator.Element>> {
var result = Array<Array<Self.Iterator.Element.Iterator.Element>>()
if self.isEmpty {return result}
var index = self.first!.startIndex
while index != self.first!.endIndex {
var subresult = Array<Self.Iterator.Element.Iterator.Element>()
for subarray in self {
subresult.append(subarray[index])
}
result.append(subresult)
index = self.first!.index(after: index)
}
return result
}
}
with usage
let arr = [[1,2,3],[4,5,6],[7,8,9]]
print(arr.transpose)
// [[1, 4, 7], [2, 5, 8], [3, 6, 9]]

Related

Find nearest smaller number in array

I would like to be able to find the nearest smaller value in an array of numbers. For instance, if I have:
[1, 4, 6, 9, 14, 39]
And I'm looking for the nearest value smaller than:
8
The function would return:
6
Additionally, if I pass a number greater than the maximum value in the array, it should return the maximum. If I pass a number smaller than the minimum, it should return nil.
I tried doing this using the first function on arrays, however this on its own doesn't produce the result I'm looking for as I would need something like this:
numbers.first(where: { $0 <= target && $1 < target })
but unfortunately, this isn't valid. Any suggestions? I know this could be done fairly trivially with a while loop, however I was hoping there would be a cleaner, functional way.
Given that the array is sorted , You need
if let value = numbers.last(where: { $0 <= target }) {
print(value)
}
This is a generic solution using binary search. The array must be sorted
extension RandomAccessCollection where Element : Comparable {
func lowerElement(of value: Element) -> Element? {
var slice : SubSequence = self[...]
while !slice.isEmpty {
let middle = slice.index(slice.startIndex, offsetBy: slice.count / 2)
if value < slice[middle] {
slice = slice[..<middle]
} else {
slice = slice[index(after: middle)...]
}
}
return slice.startIndex == self.startIndex ? nil : self[self.index(before: slice.startIndex)]
}
}
let array = [1, 4, 6, 9, 14, 39]
let result = array.lowerElement(of: 8)
print(result)

Swift: Working with for and array

I started to learn Swift but I have some problems like I want to sum two arrays and put them in a third array and I have to do it with for loop.
var ar1 = [1,3,5,7,9]
var ar2 = [2,4,6,8,10]
var ar3 : [Int] = [5]
for i in 0... ar1.count-1 {
// for loop for index
ar3[i] = ar1[i] + ar2[i]
}
But I get an out of range error. I tried a lot but I could not solve it.
Your code looks more like C or Java than swift, but you're on the right track. There are many other ways to do this, but this code is very similar to what you were trying to do:
func example() {
let array1 = [1, 3, 5, 7, 9]
let array2 = [2, 4, 6, 8, 10]
var array3 = [Int]()
for i in 0 ..< array1.count {
array3.append(array1[i] + array2[i])
}
for element in array3 {
print("\(element)")
}
}
Use "let" if the variable never changes once you defined it.
I added a loop at the bottom so you can verify the results, and also so you can see that you don't need "i" in loops.

Swift: Set<Object> fundamental operations result in copies or pointers?

I'm currently looking for some reference, outside apple's swift programming reference for the memory space of Set types and resulting fundamental operations (union, intersection, exclusion, subtraction etc)
Given the below pseudo code:
var entities = Set<GKEntity>()
var subSetA = Set<GKEntity>()
var subSetB = Set<GKEntity>()
Each subset will have a subclass of GKEntity which will be called on some routines I will use elswhere in my application.
When I use the union of these subsets, IE: I will have set as the superset of all subsets, or the union of all subsets.
Does this mean that the superset will be a copy of the elements in the subset or will they be pointers only?
I ask this for memory space usage as if the operation requires copying or allocating new memory adresses, I will need to use a different strategy of storing my elements.
From a purely structural standpoint, I assume these will be "shallow" copies meaning they will be pointers to memory adresses, but once I have created the superset as the union of all subsets, I want any removal or addition to be reflected on the superset, or down on the particular subset if the operation is made on the superset.
Hope this question is valid
Note, I'm assuming you mean this GKEntity.
Since GKEntity is a class, the sets you created will store references (pointers) to those actual GKEntity objects. So any changes to the objects in a subset will be reflected in the superset.
Here is a short piece of code that demonstrates this:
class A: IntegerLiteralConvertible, Hashable, CustomStringConvertible {
var x: Int
required init(integerLiteral value: Int) {
self.x = value
}
var hashValue: Int {
return x
}
var description: String {
return String(x)
}
}
func ==(lhs: A, rhs: A) -> Bool {
return lhs.hashValue == rhs.hashValue
}
let setA: Set<A> = [1, 2, 3, 4]
let setB: Set<A> = [5, 6, 7, 8]
print(setA) // [2, 3, 1, 4]
print(setB) // [5, 6, 7, 8]
let union = setA.union(setB)
print(union) // [2, 4, 5, 6, 7, 3, 1, 8]
setA.first!.x = 30
print(union) // [30, 4, 5, 6, 7, 3, 1, 8]
As you can see, I made a change (changed x from 2 to 30) to the first item in setA. Then I printed out union, which did contain a 30 in it.

Reduce to count elements in a dictionary in Swift

I'm trying to count the elements of my dictionary. The dictionary is of type [EKCalendar: ReminderList] where ReminderList is a class with a list property. I want to go through the dictionary and add up the count of all these lists.
My dictionary is in the property self?.reminderListsStructure.structure.
let numberOfElements = self?.reminderListsStructure.structure.reduce(0) {
accumulator, nextValue in
return accumulator.list.count + nextValue.list.count
// COMPILE ERROR: Type of expression is ambiguous without more context
}
let count = reminderListsStructure.structure.reduce(0) { $0 + $1.1.list.count }
Something like this. Not enough info though, so I'm not 100% sure it works.
I think flatMap is a more appropriate choice here:
let input = [
1: [1],
2: [1, 2],
3: [1, 2, 3],
4: [1, 2, 3, 4],
5: [1, 2, 3, 4, 5]
]
let output = input.values.flatMap{$0}.count //15
When you reduce a dictionary, the element type is a tuple of the Key and Value types, so you can use:
dictionary.reduce(0) { $0 + $1.1.list.count }
Or, you can get just the values from the dictionary and reduce that:
dictionary.values.reduce(0) { $0 + $1.list.count }
Note that since Dictionary.values returns a lazy iterator, there's not really a huge cost to using it.
More simple and clear way goes like this,
var dict = ["x" : 1 , "y" : 2, "z" : 3]
let count = dict.reduce(0, { x, element in
//maybe here some condition
//if(element.value > 1){return x}
return x + 1
})

How to add integer to array (with explicite int index) in swift?

I read swift handbook and was trying to do some exercises. But I run into a problem and I do not know if I do something wrong or if xCode 6 beta is just buggy.
// Playground - noun: a place where people can play
import Cocoa
let interestingNumbers = [
"Prime": [2, 3, 5, 7, 11, 13],
"Fibonacci": [1, 1, 2, 3, 5, 8],
"Square": [1, 4, 9, 16, 25],
]
var largest = 0
var lastLargest = Integer[]()
var index = 0
for (kind, numbers) in interestingNumbers {
for number in numbers {
if number > largest {
//lastLargest[index] = number
index++
largest = number
}
}
}
index
lastLargest
largest
As soon as I uncomment lastLargest[index] = number I do not get any results on right side in playground. Nor I get any infos about index, lastLargest or largest.
Following example does not work either:
var index2 = 0
var lastLargest2 = Integer[]()
lastLargest2[index2] = 1
index2++
lastLargest2[index2] = 2
You are appending using an out of bound array-index. Don't do that. Instead, use append:
lastLargest.append(number)
From Apple's documentation:
You can’t use subscript syntax to append a new item to the end of an array. If you try to use subscript syntax to retrieve or set a value for an index that is outside of an array’s existing bounds, you will trigger a runtime error.
When you're using explicit indexes (subscript notation) to set values in a mutable array, some value must already exist in that array at that index. When you use subscript notation, you're essentially using a 'set', rather than a 'set and add if necessary'.
As a result, you should be using:
lastLargest.insert(number, atIndex: index)
If you want to insert a new item. This will let you insert an item at the specified index, assuming your collection's size is already greater than or equal to the index you're trying to replace.