Transform a Swift sequence in to adjacent pairs
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))]
Convert an array to matrix to find adjacent elements iOS swift
In the desired output, I guess that it's missing (9,15)
, (10,11)
, (10,16)
and that (10,15)
isn't valid.
If we think about your desired output, we notice something.
Let's name width = 6, it's the "width" of your matrix.
We see the pattern:(value, value + 1)
, (value, value + width)
With some excluded tests: does (value + width) exists ? And we are not at the end of the width.
Let's, with a little reduce method:
let ptsArray = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17]
let width = 6
let tuples = ptsArray.indices.reduce(into: [(Int, Int)]()) { partialResult, anIndex in
if ptsArray.count > anIndex.advanced(by: 1) && anIndex % width != width - 1 {
let newValue = (ptsArray[anIndex], ptsArray[anIndex.advanced(by: 1)])
print(newValue)
partialResult.append(newValue)
}
if ptsArray.count > anIndex.advanced(by: width) {
let newValue = (ptsArray[anIndex], ptsArray[anIndex.advanced(by: width)])
print(newValue)
partialResult.append(newValue)
}
return
}
print(tuples)
I used "index", because in fact, points are in order here, but it could be any value, no? So let's use the index instead.
So, with something a little more generic:
extension Array {
func coupling(with width: Int) -> [(Element, Element)] {
let couples = indices.reduce(into: [(Element, Element)]()) { partialResult, anIndex in
if count > anIndex.advanced(by: 1) && anIndex % width != width - 1 {
let newValue = (self[anIndex], self[anIndex.advanced(by: 1)])
partialResult.append(newValue)
}
if count > anIndex.advanced(by: width) {
let newValue = (self[anIndex], self[anIndex.advanced(by: width)])
partialResult.append(newValue)
}
return
}
return couples
}
}
Use:
let ptsArray = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17]
let tuples2 = ptsArray. coupling(with: width)
print(tuples2)
let lettersArray = ["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P", "Q", "R"]
let tuples3 = lettersArray. coupling(with: width)
print(tuples3)
Swift: What's the best way to pair up elements of an Array
This is now available as
Sequence.chunks(ofCount: 2)
of the swift-algorithms
package
for chunk in input.chunks(ofCount: 2) {
print(chunk)
}
How to create an array of Tuples from an array of Double?
let doubles: [Double] = [0, 1, 2, 3, 4]
let tuples = zip(doubles.dropLast(), doubles.dropFirst()).map { ($0, $1) }
Result:
[(0, 1), (1, 2), (2, 3), (3, 4)]
@Edit
As pointed out in comments you can use even shorter form:
Array(zip(doubles, doubles.dropFirst()))
I'd consider NOT skipping .dropLast()
, just to make code more obvious.
Iterate through an array to find pairs of values
An alternative but similar approach as @Sulthan's answer is to use a dictionary to count occurrences rather than NSCountedSet
:
let colors = [10, 20, 20, 10, 10, 30, 50, 10, 20]
let numberOfPairs = colors
.reduce(into: [:]) { counts, num in counts[num, default: 0] += 1 }
.reduce(0) { cumsum, kv in cumsum + kv.value / 2 } // 3
Or, using shorthand argument names in the two closures:
let numberOfPairs = colors
.reduce(into: [:]) { $0[$1, default: 0] += 1 }
.reduce(0) { $0 + $1.value / 2 }
Where above, for the number occurrence count, we make use of @vacawama's answer in the Q&A that I initially used as target for dupe-marking this Q&A.
Swift Comparison in consecutive numbers inside array and its count
From this answer by Martin R, you can check how to create pairs as below,
let input = [1,2,4,6,7,10,12,13]
let output = stride(from: 0, to: input.count - 1, by: 2).map{(input[$0], input[$0 + 1])}
Now you can create differences array and find the one's count as below,
let differences = output.map({ $0.1 - $0.0 })
let onesCount = differences.filter({ $0 == 1}).count
print(differences)
print(onesCount)
Output
[1, 2, 3, 1]
2
Custom operation to split sorted array into subarrays in Swift
Your code does not work correctly because:
- You do not compare adjacent elements.
- You start by comparing the first element with itself, this can lead
to an never-terminating loop. - An empty array is not handled correctly.
Here is a working variation of your approach:
extension Array {
public func splitSorted(by condition: (Element, Element)->(Bool)) -> [[Element]] {
var result = [[Element]]()
var start = startIndex
while start != endIndex {
var end = start
repeat {
end += 1
} while end != endIndex && condition(self[end - 1], self[end])
result.append(Array(self[start..<end]))
start = end
}
return result
}
}
Example:
let arr = [1, 2, 3, 2, 3, 4, 3, 4, 5]
let split = arr.splitSorted(by: <)
print(split) // [[1, 2, 3], [2, 3, 4], [3, 4, 5]]
A generalization to Sequence
would be:
extension Sequence {
public func splitSorted(by condition: (Element, Element)->(Bool)) -> [[Element]] {
var it = makeIterator()
guard var currentElem = it.next() else {
return [] // Input sequence is empty
}
var result = [[Element]]()
var currentSegment = [currentElem]
while let nextElem = it.next() {
if condition(currentElem, nextElem) {
// Append to current segment:
currentSegment.append(nextElem)
} else {
// Start new segment:
result.append(currentSegment)
currentSegment = [nextElem]
}
currentElem = nextElem
}
result.append(currentSegment)
return result
}
}
Example (group Fibonacci numbers by same parity):
// From https://stackoverflow.com/a/40203183/1187415
let fibs = sequence(state: (0, 1),
next: { (pair: inout (Int, Int)) -> Int? in
defer { pair = (pair.1, pair.0 + pair.1) }
return pair.1
})
print(fibs.prefix(12).splitSorted(by: { ($0 - $1) % 2 == 0 }))
// [[1, 1], [2], [3, 5], [8], [13, 21], [34], [55, 89], [144]]
Is there a way to shuffle an array so that no two consecutive values are the same?
Despite appearances, this is non-trivial. As the commentator @antonio081014 points out, it's actually an algorithmic question, and (as @MartinR points out) is addressed here. Here's a very simple heuristic that (unlike the solution from @appzYourLife) is not an algorithm, but will work in most cases, and is much faster (O(n^2) rather than O(n!)). For randomness, simply shuffle the input array first:
func unSort(_ a: [String]) -> [String] {
// construct a measure of "blockiness"
func blockiness(_ a: [String]) -> Int {
var bl = 0
for i in 0 ..< a.count {
// Wrap around, as OP wants this on a circle
if a[i] == a[(i + 1) % a.count] { bl += 1 }
}
return bl
}
var aCopy = a // Make it a mutable var
var giveUpAfter = aCopy.count // Frankly, arbitrary...
while (blockiness(aCopy) > 0) && (giveUpAfter > 0) {
// i.e. we give up if either blockiness has been removed ( == 0)
// OR if we have made too many changes without solving
// Look for adjacent pairs
for i in 0 ..< aCopy.count {
// Wrap around, as OP wants this on a circle
let prev = (i - 1 >= 0) ? i - 1 : i - 1 + aCopy.count
if aCopy[i] == aCopy[prev] { // two adjacent elements match
let next = (i + 1) % aCopy.count // again, circular
// move the known match away, swapping it with the "unknown" next element
(aCopy[i], aCopy[next]) = (aCopy[next], aCopy[i])
}
}
giveUpAfter -= 1
}
return aCopy
}
var colors = ["blue", "red", "green", "red", "blue", "blue", "blue", "green"]
unSort(colors) // ["blue", "green", "blue", "red", "blue", "green", "blue", "red"]
// Add an extra blue to make it impossible...
colors = ["blue", "blue", "green", "red", "blue", "blue", "blue", "green"]
unSort(colors) //["blue", "green", "blue", "red", "blue", "blue", "green", "blue"]
Related Topics
How to Make the Memberwise Initialiser Public, by Default, for Structs in Swift
Is There a Writetofile Equivalent for Swift 3's 'Data' Type
How to Conform to Nscopying and Implement Copywithzone in Swift 2
Binding an Element of an Array of an Observableobject:'Subscript(_:)' Is Deprecated
Accessing Mkmapview Elements as Uiviewrepresentable in the Main (Contentview) Swiftui View
Unsaferawpointer Assumingmemorybound VS. Bindmemory
Does Swift Have a Null Coalescing Operator and If Not, What Is an Example of a Custom Operator
Convert String to Bool in Swift - via API or Most Swift-Like Approach
Can't Use Storyboard Custom Instantiated Window Controller
Parsing a Iso8601 String to Date in Swift
Obj-C Cocoapods + Swift Framework
Swift Function with Args... Pass to Another Function with Args
Pointers, Pointer Arithmetic, and Raw Data in Swift
How to Get the Height of a Uilabel in Swift
Checking If an Array of Custom Objects Contain a Specific Custom Object