Find next element in matrix - swift

I have simple two dimensional array with different number of elements in each row. My goal is find next element with elegant solution.
If we have the last element we should get the first one.
[
[1, 0, 234, 345, 678],
[123, 456, 789],
[0, 9]
]
678 -> 123
9 -> 1

If mat is a nested array then mat.joined() is a “lazy flat collection” of all the nested elements. That suggest to solve the problem for arbitrary collections:
Given a collection and an element of that collection, return the immediate successor of the element. The collection is viewed as cyclic, so that the successor of the last element is the first element.
A straight-forward implementation is (explanations inline):
extension Collection where Element: Comparable {
func cyclicNext(after elem: Element) -> Element? {
// Find (first) index of given element
guard var idx = firstIndex(of: elem) else {
return nil // Element not present in collection
}
formIndex(after: &idx) // Advance index by one
if idx == endIndex { // If past the end ...
idx = startIndex // ... then jump back to the start
}
return self[idx]
}
}
If the element occurs more than once in the collection then the successor of the first occurrence is returned.
This can be applied to your matrix:
let mat: [[Int]] = [[1, 0, 234, 345, 678],
[123, 456, 789],
[0, 9]]
print(mat.joined().cyclicNext(after: 1)) // 0
print(mat.joined().cyclicNext(after: 678)) // 123
print(mat.joined().cyclicNext(after: 123)) // 456
print(mat.joined().cyclicNext(after: 9)) // 1
print(mat.joined().cyclicNext(after: 999)) // nil
It can also be applied to other collections:
print([1, 3, 5, 7].cyclicNext(after: 3)) // 5
print([1, 3, 5, 7].cyclicNext(after: 7)) // 1
print("Hello World".cyclicNext(after: "W")) // "o"
print("Hello World".cyclicNext(after: "d")) // "H"

I believe this does do the job :
extension Array where Element == [Int] {
func element(after x: Int) -> Int? {
var arrayIndex = 0
while arrayIndex < self.count {
//If an array contains the searched element
if let xIndex = self[arrayIndex].firstIndex(where: { $0 == x }) {
//if the next element is in the same array
if xIndex < self[arrayIndex].count - 1 {
return self[arrayIndex][xIndex + 1]
}
//if the next element is in the next array
else if arrayIndex < self.count - 1 {
return self[arrayIndex + 1][0]
}
//if the x is the last element in the last array
else {
return self[0][0]
}
}
arrayIndex += 1
}
return nil
}
}
And here are some test cases :
let mat = [
[1, 0, 234, 345, 678],
[123, 456, 789],
[0, 9]
]
mat.element(after: 678) //123
mat.element(after: 9) //1
mat.element(after: 1) //0
mat.element(after: 0) //234
mat.element(after: 3) //nil

A possible solution is to flatten your array.
Tested on Playground:
let mat: [[Int]] = [[1, 0, 234, 345, 678],
[123, 456, 789],
[0, 9]]
func next(element: Int, in matrix: [[Int]]) -> Int? {
let flatten = matrix.flatMap({ $0 })
guard let index = flatten.firstIndex(of: element) else {
print("Element \(element) not found in matrix")
return nil
}
if index == flatten.endIndex - 1 {
return flatten.first
} else {
return flatten[index + 1 ]
}
}
let result1 = next(element: 678, in: mat)
print("End of subarray Test: \(result1)")
let result2 = next(element: 9, in: mat)
print("Last one Test: \(result2)")
let result3 = next(element: 66, in: mat)
print("Not present test: \(result3)")
let result0 = next(element: 1, in: mat)
print("First one test: \(result0)")
Output:
$>End of subarray Test: Optional(123)
$>Last one Test: Optional(1)
$>Element 66 not found in matrix
$>Not present test: nil
$>First one test: Optional(0)
I don't know if it's enough elegant for you.
One optimization would have to keep the flatten and not recalculate each time.

Related

How to perform sorting on an array without using .sort()

I am trying to find the easiest way to sort an array without using sort() function. I tried searching but i could not find any questions that were on SWIFT. I found several questions about php and javascript and so far nothing on swift.
var arr = [7,6456,2135,164,1345,4,8,5,87456,123,2,87,724,6523,1]
var arrSorted = arr
var index = arr.count
repeat {
var previousSwapIndex = 0
for i in 1..<index {
if (arrSorted[i - 1] as! Int) > (arrSorted[i] as! Int) {
let prevVal = arrSorted[i - 1]
let currentVal = arrSorted[i]
arrSorted[i] = prevVal
arrSorted[i - 1] = currentVal
previousSwapIndex = i
}
}
index = previousSwapIndex
} while (index != 0)
print(arrSorted as Array)
This method works but i am looking for something that is better than this and easier than this.
(Edit[Clarification] :- better = faster / quicker ,as this iterates 120 times before the array is sorted)
Could someone help me out?
Here's a generic implementation of insertion sort in Swift. It takes an inout array, but you should be able to modify it to return an array if that's what you want.
func sort<T: Comparable>(_ array: inout [T]) {
var i = 1
while i < array.count {
var x = array[i]
var j = i - 1
while j >= 0 && array[j] > x {
array[j+1] = array[j]
j -= 1
}
array[j+1] = x
i += 1
}
}
To use it:
var intArr = [1, 7, 3, 6, 4]
sort(&intArr)
print(intArr) // [1, 3, 4, 6, 7]
var stringArr = ["hello", "goodbye", "a", "string", "z", "another string"]
sort(&stringArr)
print(stringArr) // ["a", "another string", "goodbye", "hello", "string", "z"]
It will work on any type that conforms to Comparable.
You can find about all the different methods of sorting from this git.
https://github.com/raywenderlich/swift-algorithm-club
I checked a few and none of them are using any .sort() functions. Pick whichever feels easier for you.
var unsortedStringArray = ["B", "C", "Z", "A", "H"]
var unsortedIntArray = [7,8,3,4,5,9,1,2,6]
func sortFunction<T:Comparable>(array: [T]) -> [T]{
var unsortedArray = array
for i in 0..<unsortedArray.count {
for j in 0..<unsortedArray.count{
var temp: T
if unsortedArray[i] < unsortedArray[j] {
temp = unsortedArray[i]
unsortedArray[i] = unsortedArray[j]
unsortedArray[j] = temp
}
}
}
return unsortedArray
}
let resultStringArray = sortFunction(array: unsortedStringArray)
let resultIntArray = sortFunction(array: unsortedIntArray)
print(resultStringArray) //["A", "B", "C", "H", "Z"]
print(resultIntArray) // [1, 2, 3, 4, 5, 6, 7, 8, 9]

Find the count of element in a sequence way in swift?

This is the array of elements:
let numbers = [7, 7, 6, 6, 4, 4, 4, 5, 5, 7]
and the output will be in the given form
[2, 2, 3, 2, 1]
I tried this method:
for item in numbers {
let trueCount = numbers.filter { $0 == item }.count
print("\(item) Total Count is \(trueCount)")
}
But it's not working.
What you are looking for is something like that:
let input = [7,7,6,6,4,4,4,5,5,7]
let reduced = input.reduce(into: [(value: Int, count: Int)]()) { acc, value in
if (acc.last?.value == value) {
acc[acc.count - 1].count += 1
} else {
acc.append((value: value, count: 1))
}
}
let counts = reduced.map { $0.count }
print(counts)
Output:
[2, 2, 3, 2, 1]

Swift: Index out of range (for)

I am trying to do challange, but i stumbled on error, that my index is out of range. I do not know what can be the problem.
I tried anything that came to my mind. Thanks for any help.
func countApplesAndOranges(s: Int, t: Int, a: Int, b: Int, apples: [Int], oranges: [Int]) -> Void {
var positionApples : [Int] = apples
var positionOranges : [Int] = oranges
for i in positionApples {
positionApples[i] += a //This line
}
for i in positionOranges {
positionOranges[i] += b //This line
}
var hitApples : Int = 0
var hitOranges : Int = 0
for i in positionApples {
if i >= s && i <= t {
hitApples += 1
}
}
for i in positionOranges {
if i >= s && i <= t {
hitOranges += 1
}
}
print(hitApples)
print(hitOranges)
}
Array apples and oranges should copy to positionApples and positionOranges. Then to each item in the array, value should be added, but that just throws runtime error.
Assuming the value of apples is [2, 4, 6, 8] then the code
for i in positionApples {
print(i)
}
prints the elements 2, 4, 6, 8 and not the indices 0, 1, 2, 3 so in the second iteration of
for i in positionApples {
positionApples[i] += a //This line
}
i is 4 but the end index is actually 3 and the code will 🧨.
If you need the loop index enumerate the array
for (index, element) in positionApples.enumerated() {
print(index, element)
}
Let say, we call this method as shown below.
countApplesAndOranges(s: 3, t: 3, a: 3, b: 3, apples: [1,2,3], oranges: [4,5,6])
In the below code, possible value of i in the for loop is 1,2,3. But the actual indices of positionApples can be 0,1,2 only. When it tries to access array of index=3, it throws
"Fatal error: Index out of range"
for i in positionApples {
positionApples[i] += a //This line
}
I think this is what you wanted to do.
for i in 0 ..< positionApples.count {
positionApples[i] += a
}
This way, you can modify each element in the array.

Reverse Zip a Collection

I'm looking to do a "reverse zip" on a collection. Essentially meaning:
let a = [1, 2, 3, 4, 5, 6, 7]
let (left, right) = a.reverseZip()
print(left) // [1, 3, 5, 7]
print(right) // [2, 4, 6]
I have an implementation that seems to work (see my answer listed below), but I have two questions that I'm hoping we could discuss:
What's the correct name for this kind of operation? It's not exactly the opposite of zip (and I'm certain this pattern exists elsewhere in another functional language)
Is there a more optimal algorithm for this method?
This is a seed of an idea. Instead of returning arrays, it would be more efficient to return sequences.
Here is an Array extension that returns two LazyMapSequence<StrideTo<Int>, Element>. The advantage is that is doesn't actually generate the sequences; they are provided on demand because they're lazy. This is much like the way reversed() works on an Array.
extension Array {
func reverseZip() -> (LazyMapSequence<StrideTo<Int>, Element>, LazyMapSequence<StrideTo<Int>, Element>) {
let left = stride(from: 0, to: self.count, by: 2).lazy.map { self[$0] }
let right = stride(from: 1, to: self.count, by: 2).lazy.map { self[$0] }
return (left, right)
}
}
Example:
let a = [1, 2, 3, 4, 5, 6, 7]
let (left, right) = a.reverseZip()
// iterate left
for i in left {
print(i)
}
1
3
5
7
// turn them into arrays to print them
print(Array(left))
[1, 3, 5, 7]
print(Array(right))
[2, 4, 6]
I'm sure this can be generalized to something more general than Array. That is left as an exercise to the reader.
If you want two arrays
If you really want the two arrays, then you'd just return the result of map (taking out lazy):
extension Array {
func reverseZip() -> ([Element], [Element]) {
let left = stride(from: 0, to: self.count, by: 2).map { self[$0] }
let right = stride(from: 1, to: self.count, by: 2).map { self[$0] }
return (left, right)
}
}
Based on vacawama's idea to return lazy collections instead of arrays,
here is a possible generalization to arbitrary collections with arbitrary strides:
extension Collection {
func makeStrideIterator(stride: Int) -> AnyIterator<Element> {
precondition(stride > 0, "The stride must be positive")
var it = makeIterator()
var first = true
return AnyIterator<Element> {
if first {
first = false
} else {
// Skip `stride - 1` elements:
for _ in 1..<stride {
guard let _ = it.next() else { return nil }
}
}
return it.next()
}
}
}
The method returns an AnyIterator (which is both an iterator and a sequence)
of the collection elements at the positions
0, stride, 2*stride, ...
Unzipping an array (or any other collection) can then be done with
let a = [1, 2, 3, 4, 5]
let left = a.makeStrideIterator(stride: 2)
let right = a.dropFirst().makeStrideIterator(stride: 2)
for e in left { print(e) }
// 1, 3, 5
for e in right { print(e) }
// 2, 4
Here is an example for picking every third character from a string:
for c in "ABCDEFG".makeStrideIterator(stride: 3) {
print(c)
}
// A D G
The special case of unzipping into two arrays can then be implemented as
extension Collection {
func unzip() -> ([Element], [Element]) {
return (Array(makeStrideIterator(stride: 2)),
Array(dropFirst().makeStrideIterator(stride: 2)))
}
}
Example:
print([1, 2, 3, 4, 5].unzip())
// ([1, 3, 5], [2, 4])
extension Collection {
func reverseZip() -> ([Element], [Element]) {
var left = [Element]()
var right = [Element]()
let capacity = Double(self.count) / 2.0
left .reserveCapacity(Int(ceil(capacity)))
right.reserveCapacity(self.count / 2)
for element in self {
if left.count > right.count {
right.append(element)
} else {
left.append(element)
}
}
return (left, right)
}
}
UPDATE (03/10/18)
Thank you all for your help. Seems like a lot of you latched onto Stride, which is definitely a good way to go. I resisted this approach for 2 reasons:
I'm keen to keep this function as an extension of Collection, rather than Array
Strictly speaking, the startIndex of a given Collection isn't necessary 0, and the progression of indexes isn't necessarily an increment of 1 each time.
Martin R's approach of using a custom iterator definitely seemed like the right approach, so I used that along with the WWDC 2018's implementation of "EveryOther" to create a standard sequential iterator that simply goes along two elements in each while loop block, till it hits the end.
extension Collection {
func deinterleave() -> ([Element], [Element]) {
let smallHalf = count / 2
let bigHalf = count - smallHalf
var left = [Element]()
var right = [Element]()
left .reserveCapacity(bigHalf)
right.reserveCapacity(smallHalf)
let start = self.startIndex
let end = self.endIndex
var iter = start
while iter != end {
left.append(self[iter])
iter = index(after: iter)
if iter == end { break }
right.append(self[iter])
iter = index(after: iter)
}
return (left, right)
}
}
let a = [1,2,3,4,5,6,7]
print(a.deinterleave()) // ([1, 3, 5, 7], [2, 4, 6])
Without a doubt, there is some scope to improve this further and use vacawama's LazyMapSequence implementation, but for now this does everything I need it to.

Geting object from array of array and it's array number

I'm using Swift 2.3 and I have the following type array of arrays of my custom object called Player
`var playing = [[obj-one, obj-two],[obj-three, obj-four]]`
How would I use a for-in loop or something else so I can get the array index and the object?
I have the following:
for (index, p) in playing { -- Expression type [[Player]] is ambigious
I've also tried
for in (index, p: Player) in playing { -- same result.
and
for in (index, p) in playing as! Player { -- doesn't conform to squence type
I want to just be able to print out which array the object belongs to and then work with that current object
Use enumerated() to pair up an index and an element, like this:
let a = [["hello", "world"], ["quick", "brown", "fox"]]
for outer in a.enumerated() {
for inner in outer.element.enumerated() {
print("array[\(outer.offset)][\(inner.offset)] = \(inner.element)")
}
}
This produces the following output:
array[0][0] = hello
array[0][1] = world
array[1][0] = quick
array[1][1] = brown
array[1][2] = fox
Functional approach:
let items = [["0, 0", "0, 1"], ["1, 0", "1, 1", "1, 2"]]
items.enumerated().forEach { (firstDimIndex, firstDimItem) in
firstDimItem.enumerated().forEach({ (secondDimIndex, secondDimItem) in
print("item: \(secondDimItem), is At Index: [\(firstDimIndex), \(secondDimIndex)]")
})
}
prints:
item: 0, 0, is At Index: [0, 0]
item: 0, 1, is At Index: [0, 1]
item: 1, 0, is At Index: [1, 0]
item: 1, 1, is At Index: [1, 1]
item: 1, 2, is At Index: [1, 2]
I wouldn't use a for loop, I would do something like this:
import Foundation
var playing = [["one", "two"], ["three", "four"]]
if let index = playing.index(where: { $0.contains("two") }) {
print(index)
} else {
print("Not found")
}
This prints:
0
Or to get the entire subarray containing what you want:
if let subarray = playing.first(where: { $0.contains("three") }) {
print(subarray)
} else {
print("Not found")
}
Prints:
["three", "four"]