Any easier to read run-length Encoding in Swift? - swift

Can anyone write run-length encoding code in swift that is easier to read than the one below or at least explains the one i got from rosettecode.org ?
Here is the input& output and the code
// "WWWBWW" -> [(3, W), (1, B), (2, W)]
func encode(input: String) -> [(Int, Character)] {
return input.characters.reduce([(Int, Character)]()) {
if $0.last?.1 == $1 { var r = $0; r[r.count - 1].0++; return r }
return $0 + [(1, $1)]
}
}

It would be easier to understand if you use reduce(into:) instead:
func encode(input: String) -> [(Int, Character)] {
input.reduce(into: [(Int, Character)]()) {
// if the second element of the last tuple of the result is equal to the current element (character) of the collection
if $0.last?.1 == $1 {
// increase the first element of the last tuple tuple of the result
$0[$0.index(before: $0.endIndex)].0 += 1
} else {
// otherwise add a new tuple with a value of 1 and the current element (character) to the result
$0 += CollectionOfOne((1, $1))
}
}
}
encode(input: "WWWBWW") // [(.0 3, .1 "W"), (.0 1, .1 "B"), (.0 2, .1 "W")]
You can also extend Collection and implement a generic method/property
extension Collection where Element: Equatable {
var groupped: [(Int, Element)] {
reduce(into: []) {
if $0.last?.1 == $1 {
$0[$0.index(before: $0.endIndex)].0 += 1
} else {
$0 += CollectionOfOne((1, $1))
}
}
}
}
"WWWBWW".groupped // [(.0 3, .1 "W"), (.0 1, .1 "B"), (.0 2, .1 "W")]

Hope this make it easier for you to understand.
func encode2(input: String) -> [(Int, Character)] {
var result = [(Int, Character)]()
input.forEach { char in
if result.last?.1 == char {
result[result.count - 1].0 += 1
} else {
result.append((1, char))
}
}
return result
}

I've solved the task with the following way, which may be more clear for someone:
func compress(input: String) -> [(Int, Character)] {
var output = [(Int, Character)]()
var count: Int = 1 // count of repeated characters
var i = 0
while i < input.count { // select the current character
var j = i + 1
while j < input.count &&
input[input.index(input.startIndex, offsetBy: i)] == input[input.index(input.startIndex, offsetBy: j)] { // count repeated charactes followed the current one
count += 1
j += 1
}
output.append((count, input[input.index(input.startIndex, offsetBy: i)]))
i = j // move index for the current character to the index of the last repeated one
count = 1 // reset count
}
return output
}

Related

Finding indices of array elements that add up to a target number. What would be a way to optimize my solution?

I'm practicing this problem
Given an array of integers nums and an integer target, return indices
of the two numbers such that they add up to target.
You may assume that each input would have exactly one solution, and
you may not use the same element twice.
You can return the answer in any order.
and came up with
class Solution {
func twoSum(_ nums: [Int], _ target: Int) -> [Int] {
var indices = [Int]()
for (firstIndex, firstNum) in nums.enumerated() {
for (secondIndex, secondNum) in nums.enumerated() {
if firstNum + secondNum == target && firstIndex != secondIndex {
indices = [firstIndex, secondIndex]
}
}
}
return indices
}
}
However, it has quadratic time complexity because of the nested for-in loops. What would be a good way to optimize this to run in linear time?
Here's an idea that results in a O(n) time ans space solution.
Have a hashtable that maps elements to their indices.
As you iterate through the input array check whether target - element is in the hashtable already. If so, return the indices of the current element and target - element in the hashtable.
If not, add current element as key and its index as value to the hashtable.
Using #user1984 comment your solution should look like this:
class Solution {
func twoSum(_ nums: [Int], _ target: Int) -> [Int] {
var indices = [Int]()
var dictionary: [Int: Int] = [:]
for (index, num) in nums.enumerated()
{
let diff = target - num
if let index2 = dictionary[diff] {
return [index2, index]
}
else {
dictionary[num] = index
}
}
return []
}
}
There are several approaches (depending on your requirements and time/space constraints)
final class Solution {
// Time Complexity: O(n * (n - 1) / 2) = O(n^2)
// Auxiliary Space: O(1)
func twoSum1(_ nums: [Int], _ target: Int) -> [Int] {
for firstIndex in nums.indices {
for secondIndex in (firstIndex + 1)..<nums.count {
if nums[firstIndex] + nums[secondIndex] == target {
return [firstIndex, secondIndex]
}
}
}
return []
}
// Time Complexity: O(n log n).
// Auxiliary Space: O(n). Can be O(1) if do in-place sort
func twoSum2(_ nums: [Int], _ target: Int) -> [Int] {
let sorted = nums.sorted()
var left = 0
var right = nums.count - 1
while left < right {
if sorted[left] + sorted[right] == target {
return [left, right]
} else if sorted[left] + sorted[right] < target {
left += 1
} else {
right -= 1
}
}
return []
}
// Time Complexity: O(n). (Amortized)
// Auxiliary Space: O(n).
func twoSum3(_ nums: [Int], _ target: Int) -> [Int] {
var differences = [Int: Int]()
for (index, element) in nums.enumerated() {
let difference = target - element
if let dIndex = differences[difference] {
return [index, dIndex]
}
differences[element] = index
}
return []
}
static func test() {
// Given
let nums = [1, 5, 4, 3, 7, 9, -3]
let target = 7
let solution = Solution()
let expectedResult = [2, 3]
// When
let result1 = solution.twoSum1(nums, target)
let result2 = solution.twoSum2(nums, target)
let result3 = solution.twoSum3(nums, target)
// Then
assert(Set(result1) == Set(expectedResult))
assert(Set(result2) == Set(expectedResult))
assert(Set(result3) == Set(expectedResult))
}
}

Optimizing a String Range Search

I am currently working on an algorithm to find all ranges of a target string.
Example:
Input: s = "acfacfacf", target = "acf"
Output: [(0, 3), (3, 6), (6, 9)]
Note: the upperBound is not an index of the subarray.
This is my current solution:
extension String {
func allRanges(of string: String) -> [(Int, Int)] {
var ranges = [(Int, Int)]()
var set: Set<Int> = []
let chars = Array(self)
let target = Array(string)
for index in 0..<chars.count {
if chars[index] == string.first { set.insert(index) }
for i in set {
if index-i < target.count && chars[index] == target[index-i] {
if index-i == target.count-1 {
ranges += [(i, index+1)]
}
} else {
set.remove(i)
}
}
}
return ranges
}
}
This algorithm does well on strings like "acfacfacf" but does poorly on strings like "aaaaaaaaaaaaaaaaa" where the target is "aaaaaaaa" and the expected result is:
[(0, 8), (1, 9), (2, 10), (3, 11), (4, 12), (5, 13), (6, 14), (7, 15), (8, 16), (9, 17)]
Are there any optimizations that can be done here?
Edit: Also. I understand that using tuples here is not very Swifty, but that is not my biggest concern here.
You can iterate your collection indices dropping the last n elements (the size of your collection minus one), check if the subsequence elements is equal to the other collection, if true return the range otherwise return nil:
extension Collection where Element: Equatable {
func indices<C: Collection>(of collection: C) -> [Index] where C.Element == Element {
let size = collection.count
return indices.dropLast(size-1).filter {
self[$0..<index($0, offsetBy: size)].elementsEqual(collection)
}
}
func ranges<C: Collection>(of collection: C) -> [Range<Index>] where C.Element == Element {
let size = collection.count
return indices.dropLast(size-1).compactMap {
let range = $0..<index($0, offsetBy: size)
return self[range].elementsEqual(collection) ? range : nil
}
}
}
You can also try to optimize it using collection's method starts(with:) to avoid offseting the collection index on every single iteration:
func starts<PossiblePrefix>(with possiblePrefix: PossiblePrefix) -> Bool where PossiblePrefix : Sequence, Self.Element == PossiblePrefix.Element
extension Collection where Element: Equatable {
func ranges<C: Collection>(of collection: C) -> [Range<Index>] where C.Element == Element {
let size = collection.count
return indices.dropLast(size-1).compactMap {
self[$0...].starts(with: collection) ? $0..<index($0, offsetBy: size) : nil
}
}
}
Or zipping the lower and upper indices of the possible ranges:
extension Collection where Element: Equatable {
func ranges<C: Collection>(of collection: C) -> [Range<Index>] where C.Element == Element {
let k = collection.count-1
return zip(indices.dropLast(k),indices.dropFirst(k)).compactMap {
self[$0...].starts(with: collection) ? $0 ..< index(after: $1) : nil
}
}
}
Building off of Leo's post. There appears to be some unnecessary work with performing .start(...) and then index(...). This can be further optimized with
extension Collection where Element: Equatable {
func ranges<C: RandomAccessCollection>(of collection: C) -> [Range<Index>] where C.Element == Element {
let size = collection.count
return indices.dropLast(size-1).compactMap { idx in
var i = idx
var match = false
collection.drop { element in
match = element == self[i]
i = index(after: i)
return match
}
return match ? idx..<i : nil
}
}
}

Explanation of this prefix Sum Coding Challenge from Codility GenomicRangeQuery in Swift 4.2

Could someone explain how this prefix sum calculation work as I am confused I mean I understand that it creates an array of Ints with the prefix-sums of the letters but I do not understand how? Could someone perhaps post a more naive logic or some explanation? Or perhaps a shorter version of that MAP function as it is all confusingly complicated.
Link to the challenge just in case someone would like to have a look at it
public func solution(_ S : inout String, _ P : inout [Int], _ Q : inout [Int]) -> [Int] {
//The mapping below needs the explanation
var prefixSumA = S.map({ () -> (Character) -> Int in
var s = 0; return {
if $0 == "A" {
return (s += 1, s).1
}
return s
}
}())//until here
prefixSumA.insert(0, at: 0)
var prefixSumC = S.map({ () -> (Character) -> Int in
var s = 0; return {
if $0 == "C" {
return (s += 1, s).1
}
return s
}
}())
prefixSumC.insert(0, at: 0)
var prefixSumG = S.map({ () -> (Character) -> Int in
var s = 0; return {
if $0 == "G" {
return (s += 1, s).1
}
return s
}
}())
prefixSumG.insert(0, at: 0)
let iterations = min(P.count, Q.count)
var result = [Int]()
for i in 0...iterations - 1 {
let p = P[i]
let q = Q[i] + 1
if prefixSumA[q] - prefixSumA[p] > 0 {
result.append(1)
} else if prefixSumC[q] - prefixSumC[p] > 0 {
result.append(2)
} else if prefixSumG[q] - prefixSumG[p] > 0 {
result.append(3)
} else {
result.append(4)
}
}
return result
}
prefixSumA calculates the number of As from the start to every given index. The same can be said about prefixSumC, and prefixSumG.
For example, if the string S is "CAGCCTA", we'll have:
prefixSumA = [0, 0, 1, 1, 1, 1, 1, 2]
prefixSumC = [0, 1, 1, 1, 2, 3, 3, 3]
prefixSumG = [0, 0, 0, 1, 1, 1, 1, 1]
(Notice the zero inserted at the beginning.)
This block of code:
var prefixSumA = S.map({ () -> (Character) -> Int in
var s = 0; return {
if $0 == "A" {
return (s += 1, s).1
}
return s
}
}())
prefixSumA.insert(0, at: 0)
returns a closure that, if the character is A, would capture the current value of s (which is the last value in the array), increment it, end return it (s).
return (s += 1, s).1 is a fancy way, that could be written as:
s += 1
return s
The whole block can be written more simply:
var prefixSumA = [0]
var countOfA = 0
prefixSumA += S.map { char in
if char == "A" { countOfA += 1 }
return countOfA
}
The same can be done for prefixSumC and prefixSumG.
I tried this one and got 62%. Performance Tests Fails.
public func solution(_ S : inout String, _ P : inout [Int], _ Q : inout [Int]) -> [Int] {
// write your code in Swift 4.2.1 (Linux)
var retArr = [Int]()
var chrArr = [Character]()
for chr in S {
chrArr.append(chr)
}
for i in 0..<P.count {
var minFactor = 4
if P[i] - Q[i] == 0 {
if chrArr[P[i]] == "A"{
minFactor = 1
}else if chrArr[P[i]] == "C"{
minFactor = 2
}else if chrArr[P[i]] == "G"{
minFactor = 3
}
}else {
for j in P[i]...Q[i] {
if chrArr[j] == "A"{
minFactor = 1
break
}else if chrArr[j] == "C"{
minFactor = 2
}else if chrArr[j] == "G"{
if minFactor > 2 {
minFactor = 3
}
}
}
}
retArr.append(minFactor)
}
return retArr
}

Rotate Array in Swift

While exploring algorithms in Swift, couldn't find algorithm for array rotation in swift without using funcs shiftLeft / shiftRight.
C has this graceful algo with time complexity of O(N):
/* Function to left rotate arr[] of size n by d */
void leftRotate(int arr[], int d, int n)
{
rvereseArray(arr, 0, d-1);
rvereseArray(arr, d, n-1);
rvereseArray(arr, 0, n-1);
}
/*Function to reverse arr[] from index start to end*/
void rvereseArray(int arr[], int start, int end)
{
int temp;
while (start < end)
{
temp = arr[start];
arr[start] = arr[end];
arr[end] = temp;
start++;
end--;
}
}
I'm struggling with converting this into swift:
func rotate(array:[Int], positions:Int, arSize:Int) {
var a = array
var p = positions
var s = arSize
reverseArray(array: a, start: 0, end: p-1)
reverseArray(array: a, start: p, end: s-1)
reverseArray(array: a, start: 0, end: s-1)
}
func reverseArray(array: [Int], start:Int, end:Int) {
var a = array
var s = start
var e = end
var temp = 0
while s < e {
temp = a[s]
a[s] = a[e]
a[e] = temp
s += 1
e -= 1
}
}
As I understand, for swift, we need to specify return types.
How they should be configured without increasing space(memory) complexity? (aka, without creating new temporary arrays)
This question is different from others, because its about how returns work in swift compare to C.
Edit update:
Swift 5 or later
extension RangeReplaceableCollection {
func rotatingLeft(positions: Int) -> SubSequence {
let index = self.index(startIndex, offsetBy: positions, limitedBy: endIndex) ?? endIndex
return self[index...] + self[..<index]
}
mutating func rotateLeft(positions: Int) {
let index = self.index(startIndex, offsetBy: positions, limitedBy: endIndex) ?? endIndex
let slice = self[..<index]
removeSubrange(..<index)
insert(contentsOf: slice, at: endIndex)
}
}
extension RangeReplaceableCollection {
func rotatingRight(positions: Int) -> SubSequence {
let index = self.index(endIndex, offsetBy: -positions, limitedBy: startIndex) ?? startIndex
return self[index...] + self[..<index]
}
mutating func rotateRight(positions: Int) {
let index = self.index(endIndex, offsetBy: -positions, limitedBy: startIndex) ?? startIndex
let slice = self[index...]
removeSubrange(index...)
insert(contentsOf: slice, at: startIndex)
}
}
var test = [1,2,3,4,5,6,7,8,9,10]
test.rotateLeft(positions: 3) // [4, 5, 6, 7, 8, 9, 10, 1, 2, 3]
var test2 = "1234567890"
test2.rotateRight(positions: 3) // "8901234567"
We can use Slice
func rotLeft(a: [Int], d: Int) -> [Int] {
let slice1 = a[..<d]
let slice2 = a[d...]
return Array(slice2) + Array(slice1)
}
print(rotLeft(a:[1, 2, 3, 4, 5], d: 4))
//prints [5, 1, 2, 3, 4]
Why create a reverse function when we already have it in the Swift standard library?
My solution (derived from Leo Dabus'):
extension Array {
mutating func rotate(positions: Int, size: Int? = nil) {
let size = size ?? count
guard positions < count && size <= count else { return }
self[0..<positions].reverse()
self[positions..<size].reverse()
self[0..<size].reverse()
}
}
Approach 1:
func rotate(_ nums: inout [Int], _ k: Int) {
nums.enumerated().forEach { nums[ (k + $0) % nums.count] = $1 }
}
Approach 2:
func rotLeft(a: [Int], d: Int) -> [Int] {
var a = a
reverse(&a, 0, d)
reverse(&a, d, a.count)
reverse(&a, 0, a.count)
return a
}
func reverse(_ a: inout [Int], _ s: Int, _ r: Int) {
var r = r, s = s
while s < r {
a.swapAt(s, r - 1)
s += 1
r -= 1
}
}
To be complete, the rotation function should support negative (right) rotations and rotating more than the array's size
extension Array
{
mutating func rotateLeft(by rotations:Int)
{
// rotation irrelevant when less than 2 elements
if count < 2 { return }
// effective left rotation for negative and > count
let rotations = (rotations%count + count) % count
// no use rotating by zero
if rotations == 0 { return }
// rotate
(1..<count).reduce(0)
{ let i = ($0.0+rotations)%count; swap(&self[$0.0],&self[i]); return i }
}
mutating func reverse()
{
(0..<count/2).forEach{ swap(&self[$0],&self[count-$0-1]) }
}
}
// a is the array to be left rotated
// d is the number of unit for left rotation
func rotLeft(a: [Int], d: Int) -> [Int] {
var a = a
for index in 0...(d - 1) {
a.append(a[0])
a.remove(at: 0)
}
return a
}
// calling Function
rotLeft(a: [1,2,3,4,5], d: 4)
// OutPut
[5, 1, 2, 3, 4]
This solution rotates the element of time complexity O(n)
func rotLeft(a: [Int], d: Int) -> [Int] {
var arr = a
var size = arr.count - 1
for i in 0...size {
let newloc = (i + (arr.count - d)) % arr.count
arr[newloc] = a[i]
}
return arr
}
you shouldn't use .append(x) as in the worst case it can be
O(n) and you shouldn't use .remove(at: x) as its O(n) when you can avoid using those methods As when using them you basically get n + n + n which isn't that great
If anybody lands here after watching the Embracing Algorithms WWDC18 session by David Abrahams, here is one of the implementations of rotate from the swift/test/Prototypes/Algorithms.swift file.
extension MutableCollection where Self: BidirectionalCollection {
/// Rotates the elements of the collection so that the element
/// at `middle` ends up first.
///
/// - Returns: The new index of the element that was first
/// pre-rotation.
/// **- Complexity: O(*n*)**
#discardableResult
public mutating func rotate(shiftingToStart middle: Index) -> Index {
self[..<middle].reverse()
self[middle...].reverse()
let (p, q) = _reverseUntil(middle)
self[p..<q].reverse()
return middle == p ? q : p
}
}
This algorithms depends on reverseUntil(:) defined in the same file
extension MutableCollection where Self: BidirectionalCollection {
/// Reverses the elements of the collection, moving from each end until
/// `limit` is reached from either direction. The returned indices are the
/// start and end of the range of unreversed elements.
///
/// Input:
/// [a b c d e f g h i j k l m n o p]
/// ^
/// limit
/// Output:
/// [p o n m e f g h i j k l d c b a]
/// ^ ^
/// f l
///
/// - Postcondition: For returned indices `(f, l)`:
/// `f == limit || l == limit`
#inline(__always)
#discardableResult
internal mutating func _reverseUntil(_ limit: Index) -> (Index, Index) {
var f = startIndex
var l = endIndex
while f != limit && l != limit {
formIndex(before: &l)
swapAt(f, l)
formIndex(after: &f)
}
return (f, l)
}
}
You need to consider the scenario such as-
The number of rotation can be equal/more than the size of array you need to rotate.
To handle this scenario use modulo operator to find the actual number of rotation as you will find out rotating an array by a number equal to its size result in same array.
func rotateLeft(array:[Int],numberOfRotation:Int) -> [Int]
{
let offset = numberOfRotation % array.count
let tempResult = array[offset...] + array[..<offset]
return Array(tempResult)
}
We can do it using Array's dropFirst() and dropLast() functions.
func rotateLeft(arrToRotate: inout [Int], positions: Int){
if arrToRotate.count == 0 || positions == 0 || positions > arrToRotate.count{
print("invalid")
return
}
arrToRotate = arrToRotate.dropFirst(positions) + arrToRotate.dropLast(arrToRotate.count-positions)
}
var numbers : [Int] = [1, 2, 3, 4, 5]
rotateLeft(arrToRotate: &numbers, positions:2)
print(numbers) //prints [3, 4, 5, 1, 2]
here is a way to rotate left or right. Just call rotate on your array as shown. This does not mutate the array, if you wish to mutate the array, set the array equal to the rotation.
extension Array {
func rotate(moveRight: Bool, numOfRotations: Int) -> Array<Element>{
var arr = self
var i = 0
while i < numOfRotations {
if moveRight {
arr.insert(arr.remove(at: arr.count - 1), at: 0)
} else {
arr.append(arr.remove(at: 0))
}
i += 1
}
return arr
}
}
var arr = ["a","b","c","d","e"]
print(arr.rotate(moveRight: true, numOfRotations: 2))
// ["d", "e", "a", "b", "c"]
print(arr)
// ["a", "b", "c", "d", "e"]
arr = arr.rotate(moveRight: true, numOfRotations: 2)
print(arr)
// ["d", "e", "a", "b", "c"]

Iterate over collection two at a time in Swift

Say I have an array [1, 2, 3, 4, 5]. How can I iterate two at a time?
Iteration 1: (1, 2)
Iteration 2: (3, 4)
Iteration 3: (5, nil)
You can use a progression loop called stride(to:, by:) to iterate over your elements every n elements:
let array = Array(1...5)
let pairs = stride(from: 0, to: array.endIndex, by: 2).map {
(array[$0], $0 < array.index(before: array.endIndex) ? array[$0.advanced(by: 1)] : nil)
} // [(.0 1, {some 2}), (.0 3, {some 4}), (.0 5, nil)]
print(pairs) // "[(1, Optional(2)), (3, Optional(4)), (5, nil)]\n"
To iterate your collection subsequences instead of tuples:
extension Collection {
func unfoldSubSequences(limitedTo maxLength: Int) -> UnfoldSequence<SubSequence,Index> {
sequence(state: startIndex) { start in
guard start < self.endIndex else { return nil }
let end = self.index(start, offsetBy: maxLength, limitedBy: self.endIndex) ?? self.endIndex
defer { start = end }
return self[start..<end]
}
}
}
let array = Array(1...5)
for subsequence in array.unfoldSubSequences(limitedTo: 2) {
print(subsequence) // [1, 2] [3, 4] [5]
}
This would work on any kind of collection:
let string = "12345"
for substring in string.unfoldSubSequences(limitedTo: 2) {
print(substring) // "12" "34" "5"
}
You can use sequence() and the iterator's next() method to iterate
over pairs of consecutive elements. This works for arbitrary sequences,
not only arrays:
let a = "ABCDE"
for pair in sequence(state: a.makeIterator(), next: { it in
it.next().map { ($0, it.next()) }
}) {
print(pair)
}
Output:
("A", Optional("B"))
("C", Optional("D"))
("E", nil)
The “outer” it.next() yields the elements at even positions, or nil
(in which case it.next().map { } evaluates to nil as well, and the
sequence terminates). The “inner” it.next() yields the elements
at odd positions or nil.
As an extension method for arbitrary sequences:
extension Sequence {
func pairs() -> AnyIterator<(Element, Element?)> {
return AnyIterator(sequence(state: makeIterator(), next: { it in
it.next().map { ($0, it.next()) }
}))
}
}
Example:
let seq = (1...).prefix(5)
for pair in seq.pairs() { print(pair) }
Note that the pairs are generated lazily, no intermediate array
is created. If you want an array with all pairs then
let pairs = Array([1, 2, 3, 4, 5].pairs())
print(pairs) // [(1, Optional(2)), (3, Optional(4)), (5, nil)]
does the job.
This is not identically what was asked, but I use an extension on Sequence that generates an array of arrays chunking the original sequence by any desired size:
extension Sequence {
func clump(by clumpsize:Int) -> [[Element]] {
let slices : [[Element]] = self.reduce(into:[]) {
memo, cur in
if memo.count == 0 {
return memo.append([cur])
}
if memo.last!.count < clumpsize {
memo.append(memo.removeLast() + [cur])
} else {
memo.append([cur])
}
}
return slices
}
}
So [1, 2, 3, 4, 5].clump(by:2) yields [[1, 2], [3, 4], [5]] and now you can iterate through that if you like.
Extension to split the array.
extension Array {
func chunked(into size: Int) -> [[Element]] {
return stride(from: 0, to: count, by: size).map {
Array(self[$0 ..< Swift.min($0 + size, count)]) }
}
}
let result = [1...10].chunked(into: 2)
I personally dislike looping through half the list (mainly because of dividing), so here is how I like to do it:
let array = [1,2,3,4,5];
var i = 0;
while i < array.count {
var a = array[i];
var b : Int? = nil;
if i + 1 < array.count {
b = array[i+1];
}
print("(\(a), \(b))");
i += 2;
}
You loop through the array by incrementing by 2.
If you want to have nil in the element, you need to use optionals.
If the array would have an even number of elements, you would be able to write something like this:
for i in 0..<arr.count/2 {
print(arr[2*i...2*i+1])
}
However that's not always the case. Moreover, nil is not always compatible with the type of elements in array, like the one in your example (nil is not compatible with Int, only with Int?).
Another solution would be to extend Array and add a pair() method, which returns a tuple (tuples can be heterogenous). You can use pair to walk within all pairs in the array, or, you can extend even more the Array struct and add pairs() that return an array of tuples. Note that since the second element in the tuple is an Optional you'll need to unwrap it before use.
extension Array {
func pair(at i: Index) -> (Element, Element?) {
return (self[i], i < self.count - 1 ? self[i+1] : nil)
}
func pairs() -> [(Element, Element?)] {
guard !isEmpty else { return [] }
var result = [(Element, Element?)]()
for i in 0...arr.count/2 {
result.append(self.pair(at: 2*i))
}
return result
}
}
let arr = [1, 2, 3, 4, 5]
for i in 0...arr.count/2 {
print(arr.pair(at: 2*i))
}
for pair in arr.pairs() {
print(pair)
}
Update Both above solutions can be simplified by using map instead of manually looping:
let pairs = (0..<arr.count/2).map { (arr[$0*2], arr[$0*2+1]) }
print(pairs) // prints [(1, 2), (3, 4)]
or, for the Array extension:
extension Array {
func pair(at i: Index) -> (Element, Element?) {
return (self[i], i < self.count - 1 ? self[i+1] : nil)
}
func pairs() -> [(Element, Element?)] {
guard !isEmpty else { return [] }
return (0..<(arr.count/2 + arr.count%2)).map { pair(at: $0*2) }
}
}
let arr = [1, 2, 3, 4, 5]
print(arr.pairs()) // [(1, Optional(2)), (3, Optional(4)), (5, nil)]
You can extend Collection instead, to have this pair functionality available for all collections:
extension Collection {
func pairs() -> [(Element, Element?)] {
guard !isEmpty else { return [] }
return (0..<count/2+count%2).map {
let i1 = index(startIndex, offsetBy: $0*2)
let i2 = index(after: i1)
return (self[i1], i2 < endIndex ? self[i2] : nil)
}
}
}
Here is my solution with one reduce and a few guards
extension Array {
var touplesOfTwo: [(Element,Element?)] {
self.reduce(into: [(Element,Element?)]()) {
guard let last = $0.last else { $0.append( ($1,nil) ); return }
let lastIndex = $0.count - 1
guard let _ = last.1 else { $0[lastIndex].1 = $1; return }
$0.append( ($1,nil) )
}
}
}
let list = [1,4,3,7,2,9,6,5]
let queues = list.map { $0 }
let touplesList = queues.touplesOfTwo
print("\(touplesList)")
// [(1, Optional(4)), (3, Optional(7)), (2, Optional(9)), (6, Optional(5))]
One approach would be to encapsulate the array in a class. The return values for getting pairs of items would be optionals to protect against out-of-range calls.
Example:
class Pairs {
let source = [1, 2, 3, 4, 5] // Or set with init()
var offset = 0
func nextpair() -> (Int?, Int?) {
var first: Int? = nil
var second: Int? = nil
if offset < source.count {
first = source[offset]
offset++
}
if offset < source.count {
second = source[offset]
offset++
}
return (first, second)
}
}