How can I interleave two arrays? - arrays

If I have two arrays e.g
let one = [1,3,5]
let two = [2,4,6]
I would like to merge/interleave the arrays in the following pattern [one[0], two[0], one[1], two[1] etc....]
//prints [1,2,3,4,5,6]
let comibned = mergeFunction(one, two)
print(combined)
What would be a good way to implement the combining function?
func mergeFunction(one: [T], _ two: [T]) -> [T] {
var mergedArray = [T]()
//What goes here
return mergedArray
}

If both arrays have the same length then this is a possible solution:
let one = [1,3,5]
let two = [2,4,6]
let merged = zip(one, two).flatMap { [$0, $1] }
print(merged) // [1, 2, 3, 4, 5, 6]
Here zip() enumerates the arrays in parallel and returns a sequence
of pairs (2-element tuples) with one element from each array. flatMap() creates a 2-element array from each pair and concatenates the result.
If the arrays can have different length then you append the
extra elements of the longer array to the result:
func mergeFunction<T>(one: [T], _ two: [T]) -> [T] {
let commonLength = min(one.count, two.count)
return zip(one, two).flatMap { [$0, $1] }
+ one.suffixFrom(commonLength)
+ two.suffixFrom(commonLength)
}
Update for Swift 3:
func mergeFunction<T>(_ one: [T], _ two: [T]) -> [T] {
let commonLength = min(one.count, two.count)
return zip(one, two).flatMap { [$0, $1] }
+ one.suffix(from: commonLength)
+ two.suffix(from: commonLength)
}

If you're just looking to interleave two arrays, you could just do something like:
let maxIndex = max(one.count, two.count)
var mergedArray = Array<T>()
for index in 0..<maxIndex {
if index < one.count { mergedArray.append(one[index]) }
if index < two.count { mergedArray.append(two[index]) }
}
return mergedArray

With Swift 5, you can use one of the following Playground sample codes in order to solve your problem.
#1. Using zip(_:_:) function and Collection's flatMap(_:) method
let one = [1, 3, 5, 7]
let two = [2, 4, 6]
let array = zip(one, two).flatMap({ [$0, $1] })
print(array) // print: [1, 2, 3, 4, 5, 6]
Apple states:
If the two sequences passed to zip(_:_:) are different lengths, the resulting sequence is the same length as the shorter sequence.
#2. Using an object that conforms to Sequence and IteratorProtocol protocols
struct InterleavedSequence<T>: Sequence, IteratorProtocol {
private let firstArray: [T]
private let secondArray: [T]
private let thresholdIndex: Int
private var index = 0
private var toggle = false
init(firstArray: [T], secondArray: [T]) {
self.firstArray = firstArray
self.secondArray = secondArray
self.thresholdIndex = Swift.min(firstArray.endIndex, secondArray.endIndex)
}
mutating func next() -> T? {
guard index < thresholdIndex else { return nil }
defer {
if toggle {
index += 1
}
toggle.toggle()
}
return !toggle ? firstArray[index] : secondArray[index]
}
}
let one = [1, 3, 5, 7]
let two = [2, 4, 6]
let sequence = InterleavedSequence(firstArray: one, secondArray: two)
let array = Array(sequence)
print(array) // print: [1, 2, 3, 4, 5, 6]

/// Alternates between the elements of two sequences.
/// - Parameter keepSuffix:
/// When `true`, and the sequences have different lengths,
/// the suffix of `interleaved` will be the suffix of the longer sequence.
func interleaved<Sequence: Swift.Sequence>(
with sequence: Sequence,
keepingLongerSuffix keepSuffix: Bool = false
) -> AnySequence<Element>
where Sequence.Element == Element {
keepSuffix
? .init { () -> AnyIterator<Element> in
var iterators = (
AnyIterator( self.makeIterator() ),
AnyIterator( sequence.makeIterator() )
)
return .init {
defer { iterators = (iterators.1, iterators.0) }
return iterators.0.next() ?? iterators.1.next()
}
}
: .init(
zip(self, sequence).lazy.flatMap { [$0, $1] }
)
}
let oddsTo7 = stride(from: 1, to: 7, by: 2)
let evensThrough10 = stride(from: 2, through: 10, by: 2)
let oneThrough6 = Array(1...6)
XCTAssertEqual(
Array( oddsTo7.interleaved(with: evensThrough10) ),
oneThrough6
)
XCTAssertEqual(
Array(
oddsTo7.interleaved(with: evensThrough10, keepingLongerSuffix: true)
),
oneThrough6 + [8, 10]
)

Related

Swift Array instance method drop(at: Int)

Array in Swift has several instance methods for excluding elements, such as dropFirst(), dropLast(), drop(while:), etc. What about drop(at:)?
Note: I'd use remove(at:), but the array I'm working with is a let constant.
Note: I'd use remove(at:), but the array I'm working with is a constant.
I wouldn't let that stop you:
extension Array {
func drop(at:Int) -> [Element] {
var temp = self
temp.remove(at: at)
return temp
}
}
let numbers = [1, 2, 3, 4, 5]
print(numbers.drop(at: 2)) // [1, 2, 4, 5]
You can extend RangeReplaceableCollection protocol instead of Array type, this way you can use it on Strings as well:
extension RangeReplaceableCollection {
func drop(at offset: Int) -> SubSequence {
let index = self.index(startIndex, offsetBy: offset, limitedBy: endIndex) ?? endIndex
let next = self.index(index, offsetBy: 1, limitedBy: endIndex) ?? endIndex
return self[..<index] + self[next...]
}
}
var str = "Hello, playground"
str.drop(at: 5) // "Hello playground"
let numbers = [1, 2, 3, 4, 5]
print(numbers.drop(at: 2)) // "[1, 2, 4, 5]\n"
If you would like to accept also String.Index in your method:
extension RangeReplaceableCollection {
func drop(at index: Index) -> SubSequence {
let index = self.index(startIndex, offsetBy: distance(from: startIndex, to: index), limitedBy: endIndex) ?? endIndex
let next = self.index(index, offsetBy: 1, limitedBy: endIndex) ?? endIndex
return self[..<index] + self[next...]
}
}
var str = "Hello, playground"
str.drop(at: 0) // "ello, playground"
str.drop(at: str.startIndex) // "ello, playground"
The only thing I would add to your implementation is a guard statement with a useful error message:
extension Array {
func drop(at index: Int) -> ArraySlice<Element> {
guard indices.contains(index) else {
fatalError("Index out of range")
}
return self[0..<index] + self[(index+1)..<endIndex]
}
func drop(range: CountableRange<Int>) -> ArraySlice<Element> {
guard (indices.lowerBound, range.upperBound) <= (range.lowerBound, indices.upperBound) else {
fatalError("Range is out of the indices bounds")
}
return self[0..<range.lowerBound] + self[range.upperBound..<endIndex]
}
}
let a = [1,2,3]
a.drop(at: 3) //Fatal error: Index out of range
let b = [0,1,2,3,4,5]
b.drop(range: 1..<5) //[0, 5]
return self[0..<index] + self[index+1..<endIndex]
Ugly. Why not use the tools you're given?
extension Array {
func drop(at:Int) -> ArraySlice<Element> {
return prefix(upTo: at) + suffix(from: at+1)
}
}
let arr = [1,2,3,4,5]
let slice = arr.drop(at:2) // [1,2,4,5]
EDIT It seems Apple would prefer you to say (using partial ranges)
return self[..<at] + self[(at+1)...]
but personally I think that's uglier, and after all the methods I suggested are not deprecated or anything.
How about using a Swift extension to add drop(at:) to the Array structure?
extension Array {
func drop(at index: Int) -> ArraySlice<Element> {
precondition(indices.contains(index), "Index out of range")
return self[..<index] + self[(index+1)...]
}
}
It returns a slice of the original array without the element at the specified index.
let numbers = [1, 2, 3, 4, 5]
print(numbers.drop(at: 2))
// Prints "[1, 2, 4, 5]"
Note: You may also want to add drop(at:) to ArraySlice.

Custom operation to split sorted array into subarrays in Swift

I want to write a custom operation on a sorted Array (or Collection or Sequence, whatever) that does the following:
Starting from the beginning, it looks at each adjacent pair of elements. If the condition is met among the two, move on to the next pair, otherwise split it. So in the end, I would have an array of arrays, where the condition is satisfied among the elements within the same subarray, but not between different subarrays. Is the following correct and efficient?
extension Array {
public func splitSorted(by condition: (Element, Element)->(Bool)) -> [[Element]] {
var result = [[Element]]()
var start = 0
var end = 0
while end != self.count - 1 {
while end < self.count && condition(self[start], self[end]) {
end += 1
}
result.append(Array(self[start..<end]))
start = end
}
return result
}
}
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 any easy way to merge two arrays in swift along with removing duplicates?

Basically I need a version of appendContentsOf: which does not append duplicate elements.
Example
var a = [1, 2, 3]
let b = [3, 4, 5]
a.mergeElements(b)
//gives a = [1, 2, 3, 4, 5] //order does not matter
Simply :
let unique = Array(Set(a + b))
Swift 5
Updated
In case you need to combine multiple arrays.
func combine<T>(_ arrays: Array<T>?...) -> Set<T> {
return arrays.compactMap{$0}.compactMap{Set($0)}.reduce(Set<T>()){$0.union($1)}
}
Usage examples:
1.
let stringArray1 = ["blue", "red", "green"]
let stringArray2 = ["white", "blue", "black"]
let combinedStringSet = combine(stringArray1, stringArray2)
// Result: {"green", "blue", "red", "black", "white"}
let numericArray1 = [1, 3, 5, 7]
let numericArray2 = [2, 4, 6, 7, 8]
let numericArray3 = [2, 9, 6, 10, 8]
let numericArray4: Array<Int>? = nil
let combinedNumericArray = Array(combine(numericArray1, numericArray2, numericArray3, numericArray4)).sorted()
// Result: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
This is commonly called a union, which is possible in Swift using a Set:
let a = [1, 2, 3]
let b = [3, 4, 5]
let set = Set(a)
let union = set.union(b)
Then you can just convert the set into an array:
let result = Array(union)
Swift 4.0 Version
extension Array where Element : Equatable {
public mutating func mergeElements<C : Collection>(newElements: C) where C.Iterator.Element == Element{
let filteredList = newElements.filter({!self.contains($0)})
self.append(contentsOf: filteredList)
}
}
As mentioned: The array passed to the function is the array of object that will be omitted from the final array
Swift 3.0 version of the accepted answer.
extension Array where Element : Equatable{
public mutating func mergeElements<C : Collection>(newElements: C) where C.Generator.Element == Element{
let filteredList = newElements.filter({!self.contains($0)})
self.append(contentsOf: filteredList)
}
}
Note: Worth saying here that the array passed to the function is the array of object that will be omitted from the final array. Important if your merging an array of objects where the Equatable property may be the same but others may differ.
An Array extension can be created to do this.
extension Array where Element : Equatable{
public mutating func mergeElements<C : CollectionType where C.Generator.Element == Element>(newElements: C){
let filteredList = newElements.filter({!self.contains($0)})
self.appendContentsOf(filteredList)
}
}
Of course, this is useful for only Equatable elements.
I combined my extension of Sequence and Array with this answer to provide easy syntax when merging arrays with custom objects by a single property:
extension Dictionary {
init<S>(_ values: S, uniquelyKeyedBy keyPath: KeyPath<S.Element, Key>) where S : Sequence, S.Element == Value {
let keys = values.map { $0[keyPath: keyPath] }
self.init(uniqueKeysWithValues: zip(keys, values))
}
}
// Unordered example
extension Sequence {
func merge<T: Sequence, U: Hashable>(mergeWith: T, uniquelyKeyedBy: KeyPath<T.Element, U>) -> [Element] where T.Element == Element {
let dictOld = Dictionary(self, uniquelyKeyedBy: uniquelyKeyedBy)
let dictNew = Dictionary(mergeWith, uniquelyKeyedBy: uniquelyKeyedBy)
return dictNew.merging(dictOld, uniquingKeysWith: { old, new in old }).map { $0.value }
}
}
// Ordered example
extension Array {
mutating func mergeWithOrdering<U: Hashable>(mergeWith: Array, uniquelyKeyedBy: KeyPath<Array.Element, U>) {
let dictNew = Dictionary(mergeWith, uniquelyKeyedBy: uniquelyKeyedBy)
for (key, value) in dictNew {
guard let index = firstIndex(where: { $0[keyPath: uniquelyKeyedBy] == key }) else {
append(value)
continue
}
self[index] = value
}
}
}
Test:
#testable import // Your project name
import XCTest
struct SomeStruct: Hashable {
let id: Int
let name: String
}
class MergeTest: XCTestCase {
let someStruct1 = SomeStruct(id: 1, name: "1")
let someStruct2 = SomeStruct(id: 2, name: "2")
let someStruct3 = SomeStruct(id: 2, name: "3")
let someStruct4 = SomeStruct(id: 4, name: "4")
var arrayA: [SomeStruct]!
var arrayB: [SomeStruct]!
override func setUp() {
arrayA = [someStruct1, someStruct2]
arrayB = [someStruct3, someStruct4]
}
func testMerging() {
arrayA = arrayA.merge(mergeWith: arrayB, uniquelyKeyedBy: \.id)
XCTAssert(arrayA.count == 3)
XCTAssert(arrayA.contains(someStruct1))
XCTAssert(arrayA.contains(someStruct3))
XCTAssert(arrayA.contains(someStruct4))
}
func testMergingWithOrdering() {
arrayA.mergeWithOrdering(mergeWith: arrayB, uniquelyKeyedBy: \.id)
XCTAssert(arrayA.count == 3)
XCTAssert(arrayA[0] == someStruct1)
XCTAssert(arrayA[1] == someStruct3)
XCTAssert(arrayA[2] == someStruct4)
}
}

Getting the most frequent value of an array

I have an Array of numbers and I want to know which number is most frequent in this array. The array sometimes has 5-6 integers, sometimes it has 10-12, sometimes even more - also the integers in the array can be different. So I need a function which can work with different lengths and values of an array.
One example:
myArray = [0, 0, 0, 1, 1]
Another example:
myArray = [4, 4, 4, 3, 3, 3, 4, 6, 6, 5, 5, 2]
Now I am searching for a function which gives out 0 (in the first example) as Integer, as it is 3 times in this array and the other integer in the array (1) is only 2 times in the array. Or for the second example it would be 4.
It seems pretty simple, but I cannot find a solution for this. Found some examples in the web, where the solution is to work with dictionaries or where the solution is simple - but I cannot use it with Swift 3 it seems...
However, I did not find a solution which works for me. Someone has an idea how to get the most frequent integer in an array of integers?
You can also use the NSCountedSet, here's the code
let nums = [4, 4, 4, 3, 3, 3, 4, 6, 6, 5, 5, 2]
let countedSet = NSCountedSet(array: nums)
let mostFrequent = countedSet.max { countedSet.count(for: $0) < countedSet.count(for: $1) }
Thanks to #Ben Morrow for the smart suggestions in the comments below.
let myArray = [4, 4, 4, 3, 3, 3, 4, 6, 6, 5, 5, 2]
// Create dictionary to map value to count
var counts = [Int: Int]()
// Count the values with using forEach
myArray.forEach { counts[$0] = (counts[$0] ?? 0) + 1 }
// Find the most frequent value and its count with max(by:)
if let (value, count) = counts.max(by: {$0.1 < $1.1}) {
print("\(value) occurs \(count) times")
}
Output:
4 occurs 4 times
Here it is as a function:
func mostFrequent(array: [Int]) -> (value: Int, count: Int)? {
var counts = [Int: Int]()
array.forEach { counts[$0] = (counts[$0] ?? 0) + 1 }
if let (value, count) = counts.max(by: {$0.1 < $1.1}) {
return (value, count)
}
// array was empty
return nil
}
if let result = mostFrequent(array: [1, 3, 2, 1, 1, 4, 5]) {
print("\(result.value) occurs \(result.count) times")
}
1 occurs 3 times
Update for Swift 4:
Swift 4 introduces reduce(into:_:) and default values for array look ups which enable you to generate the frequencies in one efficient line. And we might as well make it generic and have it work for any type that is Hashable:
func mostFrequent<T: Hashable>(array: [T]) -> (value: T, count: Int)? {
let counts = array.reduce(into: [:]) { $0[$1, default: 0] += 1 }
if let (value, count) = counts.max(by: { $0.1 < $1.1 }) {
return (value, count)
}
// array was empty
return nil
}
if let result = mostFrequent(array: ["a", "b", "a", "c", "a", "b"]) {
print("\(result.value) occurs \(result.count) times")
}
a occurs 3 times
The most frequent value is called the "mode". Here's a concise version:
let mode = myArray.reduce([Int: Int]()) {
var counts = $0
counts[$1] = ($0[$1] ?? 0) + 1
return counts
}.max { $0.1 < $1.1 }?.0
Whether that's considered "unreadable" or "elegant" depends on your feelings towards higher order functions. Nonetheless, here it is as a generic method in an extension on Array (so it'll work with any Hashable element type):
extension Array where Element: Hashable {
var mode: Element? {
return self.reduce([Element: Int]()) {
var counts = $0
counts[$1] = ($0[$1] ?? 0) + 1
return counts
}.max { $0.1 < $1.1 }?.0
}
}
Simply remove the .0 if you'd rather have a tuple that includes the count of the mode.
My take on it with Swift 5:
extension Collection {
/**
Returns the most frequent element in the collection.
*/
func mostFrequent() -> Self.Element?
where Self.Element: Hashable {
let counts = self.reduce(into: [:]) {
return $0[$1, default: 0] += 1
}
return counts.max(by: { $0.1 < $1.1 })?.key
}
}
I have tried the following code. It helps especially when the max count is applicable for 2 or more values.
var dictionary = arr.reduce(into: [:]) { counts, number in counts[number, default: 0] += 1}
var max = dictionary.values.max()!
dictionary = dictionary.filter{$0.1 == max}
mode = dictionary.keys.min()!
func mostR(num : [Int]) -> (number : Int , totalRepeated : Int)
{
var numberTofind : Int = 0
var total : Int = 0
var dic : [Int : Int] = [:]
for index in num
{
if let count = dic[index]
{
dic[index] = count + 1
}
else
{
dic[index] = 1
}
}
var high = dic.values.max()
for (index , count) in dic
{
if dic[index] == high
{
numberTofind = index
top.append(count)
total = count
}
}
return (numberTofind , total)
}
var array = [1,22,33,55,4,3,2,0,0,0,0]
var result = mostR(num : [1,22,3,2,43,2,11,0,0,0])
print("the number is (result.number) and its repeated by :(result.totalRepeated)" )
Here is an encapsulated/reusable method.
extension Array where Element: Hashable {
/// The mode will be nil when the array is empty.
var mode: Element? {
var counts: [Element: Int] = [:]
forEach { counts[$0] = (counts[$0] ?? 0) + 1 }
if let (value, count) = counts.max(by: {$0.1 < $1.1}) {
print("\(value) occurs \(count) times")
return value
} else {
return nil
}
}
}
usage:
print([3, 4, 5, 6, 6].mode) // 6
Keep track of each occurrence, counting the value of each key in a dictionary. This case is exclusive for integers. Will update this method using generics.
func mostCommon(of arr: [Int]) -> Int {
var dict = [Int:Int]()
arr.forEach {
if let count = dict[$0] {
dict[$0] = count + 1
} else {
dict[$0] = 1
}
}
let max = dict.values.max()
for (_ , value) in dict {
if value == max {
return value
}
}
return -1
}

Store first few elements in array in another array if they exist

Given an array that contains any number of objects, how could you cleanly and safely get the first 3 elements out of it to store in a new array? If the array does not contain at least 3 elements, it should not trigger a runtime exception, instead it should only add the number of elements in the array to the new array.
I thought this might work, but it won't compile in Xcode 7, and if it did I don't imagine it would behave safely as I desire:
let arr1 = [1, 2, 3, 4, 5]
let arr2 = arr1[0..<3]
//Expected: arr == [1, 2, 3]
let arr1 = [1, 2]
let arr2 = arr1[0..<3]
//Expected: arr2 == [1, 2]
let arr1 = [Int]()
let arr2 = arr1[0..<3]
//Expected: arr2 == []
Of course one could always do something like this, or you could use a for loop, but neither is clean and concise. I want a to find a swiftier way.
let arr1 = [1, 2]
var arr2 = [Int]()
if photos.count > 0 {
arr2.append(arr1[0])
}
if photos.count > 1 {
arr2.append(arr1[1])
}
if photos.count > 2 {
arr2.append(arr1[2])
}
I think the simplest way would be
let arr2 = arr1.prefix(3)
Another approach is to use a function…
import Swift
let arr1 = [1, 2, 3, 4, 5]
let arr2 = [1, 2]
let arr3 = [Int]()
func firstThree(data: [Int]) -> [Int] {
var results = [Int]()
for (index, number) in data.enumerate() {
if (index < 3) {
results.append(number)
}
}
return results
}
print(firstThree(arr1))
print(firstThree(arr2))
print(firstThree(arr3))
This prints:
[1, 2, 3]
[1, 2]
[]
A slightly better approach would be to use generics and get an N number of items:
func genericFirstItems<T>(array: [T], numberOfItems: Int) -> [T] {
var results = [T]()
for (index, item) in array.enumerate() {
if index < numberOfItems {
results.append(item)
}
}
return results
}
print(genericFirstItems(arr1, numberOfItems: 3))
print(genericFirstItems(arr2, numberOfItems: 3))
print(genericFirstItems(arr3, numberOfItems: 3))
This has the same output.
[1, 2, 3]
[1, 2]
[]
You can implement what you want with an extension to Array:
extension Array {
func safeRange(range : Range<Int>) -> ArraySlice<Element> {
guard range.startIndex >= 0 && range.endIndex >= 0 else {
fatalError("Ranges with negative numbers aren't supported")
}
var shrinkingRange = range
while shrinkingRange.endIndex > shrinkingRange.startIndex + 1 {
if shrinkingRange.endIndex <= self.count {
return self[shrinkingRange]
}
shrinkingRange.endIndex = shrinkingRange.endIndex - 1
}
return []
}
}
The examples you gave behave as expected:
let arr1 = [1, 2, 3, 4, 5]
let arr2 = arr1.safeRange(0..<3)
//Expected: arr == [1, 2, 3]
let arr3 = [1, 2]
let arr4 = arr3.safeRange(0..<3)
//Expected: arr2 == [1, 2]
let arr5 = [Int]()
let arr6 = arr5.safeRange(0..<3)
//Expected: arr2 == []
You could also use filter:
extension CollectionType where Generator.Element : Equatable, Index == Int {
func safeRange(range : Range<Int>) -> [Self.Generator.Element] {
return self.filter {
let index = self.indexOf($0)!
return index >= range.startIndex && index < range.endIndex
}
}
}
(This may fail if your array contains duplicate elements, since indexOf returns the index of the first instance.)

Resources