how do I reduce number of for loops? - arrays

let array = [1, 2, 3, 6, 9, 4]
let sumOf = 8
func sumOfArrayFuntion(_ array: [Int], sumOf: Int) -> Bool {
var isTrue = false
for i in 0...array.count-1 {
for j in 0...array.count-1 {
for k in 0...array.count-1 {
if array[i] + array[j] + array[k] == sumOf {
isTrue = true
}
}
}
}
return isTrue
}
sumOfArrayFuntion(array, sumOf: sumOf)
function returns true if any three integers in an array add to sumOf variable, this code is not efficient, more importantly how do I think if being efficient.

Don’t start each loop at zero. You are wasting a lot of time checking invalid combinations. Also stop each loop at the appropriate point. And you can return as soon as you find a match.
I’ve also updated the code to cut down on the number of additions.
let array = [1, 2, 3, 6, 9, 4]
let sumOf = 8
func sumOfArrayFuntion(_ array: [Int], sumOf: Int) -> Bool {
guard array.count >= 3 else { return false }
for i in 0..<array.count-2 {
for j in i+1..<array.count-1 {
let sum = array[i] + array[j]
for k in j+1..<array.count {
if sum + array[k] == sumOf {
return true
}
}
}
}
return false
}
sumOfArrayFuntion(array, sumOf: sumOf)

Related

Remove duplicates from array of Int in Swift (for-in-loop)

There are a lot of methods to remove duplicates from an array in swift, but I'm trying to use a for in loop to manage that.
Can any one explain, why this code doesn't work?
Fatal error: Index out of range
func deleteDuplicates(array: [Int]) -> [Int] {
var newArray = array
for i in 0 ..< newArray.count - 1 {
for j in i + 1 ..< newArray.count {
if newArray[i] == newArray[j] {
newArray.remove(at: j)
}
}
}
return newArray
}
array1 = [0, 1, 8, 3, 4, 4, 3, 6, 7, 11, 4, 5, 5, 8]
deleteDuplicates(array: array1)
The problematic part is to iterate over an array and update that array at the same time. In this case, removing an element while iterating.
Removing an element decreases array length (count) and also changes indices. Therefore in
for j in i + 1 ..< array.count {
if array[i] == array[j] {
newArray.remove(at: j)
}
}
After removing the first index, your other indices become invalid. Note that count is always read only once, before the actual iteration.
This is one of the reasons why removing elements during iteration is dangerous and complicated. You could fix it by maintaining the number of removed elements and update indices accordingly. Or you can iterate backwards:
var newArray = array
for i in (0 ..< newArray.count - 1).reversed() {
for j in (i + 1 ..< newArray.count).reversed() {
if newArray[i] == newArray[j] {
newArray.remove(at: j)
}
}
}
return newArray
You are still changing indices and count but since you are iterating backwards, you only change the indices that have been already used.
In general it's simple and safer to build a new array instead of updating the current one:
var newArray: [Int] = []
for value in array {
if !newArray.contains(value) {
newArray.append(value)
}
}
return newArray
which can be very reduced in complexity (performance) by using a Set to keep added elements:
var newArray: [Int] = []
var foundElements: Set<Int> = []
for value in array {
if foundElements.insert(value).inserted {
newArray.append(value)
}
}
return newArray
Which can be simplified using filter:
var foundElements: Set<Int> = []
return array.filter { foundElements.insert($0).inserted }
One Reason is you are modifying the same array on which you are running two for loops. To understand in details, use the debugger as Martin says. And
This can be done using one for loop and Set:
var newArray = [0, 1, 8, 3, 4, 4, 3, 6, 7, 11, 4, 5, 5, 8]
var array = Set<Int>()
for i in newArray {
array.insert(i)
}
print(array)
output:
[4, 5, 3, 0, 1, 8, 6, 11, 7]
Here is one simple approach that you can try.
func removeDuplicates(_ nums: inout [Int]) {
var arrLen = nums.count
var index = 0
while(index < arrLen - 1){
if nums[index] == nums[index+1] {
nums.remove(at: index)
arrLen = arrLen - 1
}else{
index = index+1
}
}
}
It removes duplicates inplace without the need for new array being created.

Find if sequence of elements exists in array

Is it possible to find if a sequence of elements in an array exists?
Lets take some digits from the Pi,
let piDigits=[3,1,4,1,5,9,2,6,5,3,5,8,9,7,9,3,2,3,8,4,6,2,6,4,3,3,8,3,2,7,9,5,0,2,8,8,4,1,9,7,1,6,9,3,9,9,3,7,5,1,0,5,8,2,0,9,7,4,9,4,4]
Now, i want to find if, 5 and 9 exist as sequence elements in the array- in this case they do, once, in positions 4 & 5.
Ideally, i wouldn't like to iterate over the array with a loop, i would like something similar to array.contains(element) .
#Bawpotter, the code snippet:
for element in piDigits{ //check every element
if element == 5 { //if element is equal with the element i want
var currentPosition = piDigits.index(of: element) //get the position of that element
if piDigits[currentPosition!+1] == 9 { //if the element at the next position is equal to the other element i want
print("true") // it prints true 7 times, instead of 1!
}
}
}
You can filter your indices where its subsequence elementsEqual is true:
extension Collection where Element: Equatable {
func firstIndex<C: Collection>(of collection: C) -> Index? where C.Element == Element {
guard !collection.isEmpty else { return nil }
let size = collection.count
return indices.dropLast(size-1).first {
self[$0..<index($0, offsetBy: size)].elementsEqual(collection)
}
}
func indices<C: Collection>(of collection: C) -> [Index] where C.Element == Element {
guard !collection.isEmpty else { return [] }
let size = collection.count
return indices.dropLast(size-1).filter {
self[$0..<index($0, offsetBy: size)].elementsEqual(collection)
}
}
func range<C: Collection>(of collection: C) -> Range<Index>? where C.Element == Element {
guard !collection.isEmpty else { return nil }
let size = collection.count
var range: Range<Index>!
guard let _ = indices.dropLast(size-1).first(where: {
range = $0..<index($0, offsetBy: size)
return self[range].elementsEqual(collection)
}) else {
return nil
}
return range
}
func ranges<C: Collection>(of collection: C) -> [Range<Index>] where C.Element == Element {
guard !collection.isEmpty else { return [] }
let size = collection.count
return indices.dropLast(size-1).compactMap {
let range = $0..<index($0, offsetBy: size)
return self[range].elementsEqual(collection) ? range : nil
}
}
}
[1, 2, 3, 1, 2].indices(of: [1,2]) // [0,3]
[1, 2, 3, 1, 2].ranges(of: [1,2]) // [[0..<2], [3..<5]]
If you only need to check if a collection contains a subsequence:
extension Collection where Element: Equatable {
func contains<C: Collection>(_ collection: C) -> Bool where C.Element == Element {
guard !collection.isEmpty else { return false }
let size = collection.count
for i in indices.dropLast(size-1) where self[i..<index(i, offsetBy: size)].elementsEqual(collection) {
return true
}
return false
}
}
[1, 2, 3].contains([1, 2]) // true
A very simple implementation using linear search:
let piDigits: [Int] = [3,1,4,1,5,9,2,6,5,3,5,8,9,7,9,3,2,3,8,4,6,2,6,4,3,3,8,3,2,7,9,5,0,2,8,8,4,1,9,7,1,6,9,3,9,9,3,7,5,1,0,5,8,2,0,9,7,4,9,4,4]
let searchedSequence: [Int] = [5, 9]
var index = 0
var resultIndices: [Int] = []
while index < (piDigits.count - searchedSequence.count) {
let subarray = piDigits[index ..< (index + searchedSequence.count)]
if subarray.elementsEqual(searchedSequence) {
resultIndices.append(index)
}
index += 1
}
print("Result: \(resultIndices)")
There are other variants as well, you could, for example, keep dropping the first character from piDigits during iteration and check whether piDigits start with the searchedSequence.
If performance is critical, I recommend using a string searching algorithm, e.g. Aho-Corasick (see https://en.wikipedia.org/wiki/String_searching_algorithm) which builds a state machine first for fast comparison (similar to regular expressions).
Let's see how regular expressions can be used:
let searchedSequences: [[Int]] = [[5, 9], [7], [9, 2]]
let stringDigits = piDigits.map { String($0) }.joined()
let stringSearchedSequences = searchedSequences.map { sequence in sequence.map { String($0) }.joined() }
let regularExpressionPattern = stringSearchedSequences.joined(separator: "|")
let regularExpression = try! NSRegularExpression(pattern: regularExpressionPattern, options: [])
let matches = regularExpression.matches(in: stringDigits, options: [], range: NSRange(location: 0, length: stringDigits.characters.count))
let matchedIndices = matches.map { $0.range.location }
print("Matches: \(matchedIndices)")
The downside of the approach is that it won't search overlapping ranges (e.g. "592" matches two ranges but only one is reported).
Inside the contains method iterates over the array and here you have to do the same thing. Here an example:
extension Array where Element: Equatable {
func contains(array elements: [Element]) -> Int {
guard elements.count > 0 else { return 0 }
guard count > 0 else { return -1 }
var ti = 0
for (index, element) in self.enumerated() {
ti = elements[ti] == element ? ti + 1 : 0
if ti == elements.count {
return index - elements.count + 1
}
}
return -1
}
}
And here how to use it:
let index = [1, 4, 5, 6, 6, 9, 6, 8, 10, 3, 4].contains(array: [6, 8, 10])
// index = 6
let index = [1, 4, 5, 6, 6, 9, 6, 8, 10, 3, 4].contains(array: [6, 8, 1])
// index = -1
let firstSeqNum = 5
let secondSeqNum = 9
for (index, number) in array.enumerated() {
if number == firstSeqNum && array[index+1] == secondSeqNum {
print("The sequence \(firstSeqNum), \(secondSeqNum) was found, starting at an index of \(index).")
}
}
Since there's no built-in method for this, this would be your best option.

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
}

Bubble sorting an array in Swift, compiler error on swap

I wrote a really simple bubble sort for a card game. It takes an array of "Card" objects, each of which has a an "order" attribute which indicates the value to be sorted against for the game in question.
The following code stopped compiling some time between Swift Beta 1 and Beta 6, and I'm not exactly sure why.
///Sort the cards array by order
func sortCards(cards: Array<Card>) -> Array<Card> {
var sorted = false
while sorted == false {
sorted = true
for i in 0...cards.count - 2 {
if cards[i].order > cards[i+1].order {
sorted = false
var first = cards[i]
var second = cards[i + 1]
cards[i] = second //ERROR
cards[i + 1] = first //ERROR
}
}
}
return cards
}
The lines where the swap occurs bombs out with a very cryptic message:
#!value $T5 is not identical to 'Card'
What changed, and what am I doing wrong here?
Bonus question: How am I supposed to understand the error message?
Function parameters are by default constant (as if declared with let).
If you want to modify the parameter inside your function, you have to declare it as a variable:
func sortCards(var cards: Array<Card>) -> Array<Card> { ...
Note that only the local parameter cards is modified, not the array passed as
an argument to the function (which seems to be your intention because the function
returns a new array).
I played with the following using swift 3. Hope it'll help some people who come here.
bubble sort:
func bubble(arr: inout [Int]) {
for i in (1..<arr.count).reversed() {
for j in 0..<i where arr[j] > arr[j + 1] {
swap(&arr[j], &arr[j + 1])
}
}
}
using stride:
func bubbleStride(arr: inout [Int]) {
for i in stride(from: arr.count - 1, to: 1, by: -1) {
for j in 0..<i where arr[j] > arr[j + 1] {
swap(&arr[j], &arr[j + 1])
}
}
}
using while:
func bubbleWhile(arr: inout [Int]) {
var i = arr.count - 1
while(i > 0) {
var j = 0
while(j < i) {
if arr[j] > arr[j + 1] {
swap(&arr[j], &arr[j + 1])
}
j += 1
}
i -= 1
}
}
This can be used to generate a random array of integers:
import Cocoa
func ints(cnt: Int, ceiling: Int) -> [Int] {
let arr = Array(repeating: 0, count: cnt)
return arr.map { _ in Int(arc4random_uniform(UInt32(ceiling))) }
}
E.g.:
let a = ints(cnt: 10, ceiling: 100)
print(a)
var b = a
bubble(arr: &b)
print(b)
output:
[13, 30, 68, 19, 1, 4, 28, 65, 96, 13]
[1, 4, 13, 13, 19, 28, 30, 65, 68, 96]
If you declare the function like this, then the array is inmutable. You need to use the keyword inoutlike this:
func sortCards(inout cards: Array<Card>) -> Array<Card> {
//code
}
Then you call it with &:
sortCards(&myArray)
Explanation
The whole model of Arrays and pass by value/reference has changed during the beta process.
In beta 1 arrays passed into subroutines were only kind of passed by value. Arrays passed by value (and let arrays) were still modifiable as long as you didn't change the length of the array, thus breaking the pass-by-value rules and allowing your original code to work.
In beta 4 I believe it was, they changed arrays to effectively always be passed by value and changed constant arrays (let) do be truly unmodifiable, which resulted in your code not working and breaking in the compile phase.
The inout keyword changes the array to be passed by reference instead of by value and changes it from being implicitly defined with let to defined with var, which means the array is now mutable, and changes to the array are seen by the caller.
Here is bubble sort implemented in swift 4.0.2
var array = [15,11,20,14,12,13,17,16,18,19]
var sortedArray = Array(array)
var sortedAboveIndex = array.count
for i in 0 ..< sortedAboveIndex-1 {
for j in 0 ..< sortedAboveIndex-i-1 {
if (sortedArray[j] > sortedArray[j+1]) {
sortedArray.swapAt(j, j+1)
}
}
}
print(sortedArray)
if any queries on above code please comment below
The function always runs O(n^2) time even if the array is sorted. It can be optimized by stopping the algorithm if inner loop didn’t cause any swap.
func main() {
var array: [Int] = [1, 3, 15, 6, 8, 12, 10, 33, 2, 88]
var swapped = false
for i in 0..<array.count {
for j in 0..<array.count - i - 1 {
if array[j] > array[j + 1] {
// let temp = array[j]
// array[j] = array[j+1]
// array[j+1] = temp
array.swapAt(j, j + 1)
swapped = true
}
}
if swapped == false {
break
}
}
print(array)
}
Swift 5: Generic bubble sort method,
func bubbleSort<T: Comparable>(with array: inout [T]) -> [T] {
for i in 1..<array.count {
for j in 0..<array.count-i where array[j] > array[j+1] {
array.swapAt(j, j+1)
}
}
return array
}
Input:-
var intArray = [8, 3, 5, 10, 4, -1, 17, 3, 18, 10]
var floatArray = [12.231, 12.23, 14.5, 3.4, 67.899, 0.0, -1.234]
var doubleArray = [123.43555, 123.1223332, -121.2212, 23.343434, 1.232434]
var stringArray = ["Ratheesh", "Srini", "Thangu", "Muthu", "Gopi"]
print(bubbleSort(with: &intArray))
print(bubbleSort(with: &floatArray))
print(bubbleSort(with: &doubleArray))
print(bubbleSort(with: &stringArray))
Output:-
[-1, 3, 3, 4, 5, 8, 10, 10, 17, 18]
[-1.234, 0.0, 3.4, 12.23, 12.231, 14.5, 67.899]
[-121.2212, 1.232434, 23.343434, 123.1223332, 123.43555]
["Gopi", "Muthu", "Ratheesh", "Srini", "Thangu"]
100% working tested code
func bubbleSort(arrValue:[Int])->[Int]{
var arr = arrValue
for i in 0..<arr.count-1{
for j in 0..<arr.count-i-1{
if arr[j] > arr[j+1]{
arr.swapAt(j, j+1)
}
}
}
print(arr)
return arr
}
Here is a way to sort in place anything Comparable.
extension Array where Element: Comparable {
mutating func bubble(by areInIncrreasingOrder: (Element, Element) -> Bool) {
for i in (0 ..< count) {
for j in (0 ..< count - i - 1) where !areInIncrreasingOrder(self[j], self[j + 1]) {
swapAt(j, j + 1)
}
}
}
}
Quick check:
var arr = (0...8).map{ _ in Int.random(in: 0...100)}
arr
arr.bubble(by: <)
arr.sorted(by: <)

Swift - Generate combinations with repetition

I'm trying to generate a nested array containing all combinations with repetition in Apple's Swift programming language.
An detailed explanation of combinations with repetition can be found near the bottom of this page: http://www.mathsisfun.com/combinatorics/combinations-permutations.html
Briefly; order does not matter, and we can repeat
n = the set of things we are choosing form
r = the number of things we are choosing
I want to create a function that will generate a nested array containing all combinations with repetition for any (small) values of n and r.
If there are n=3 things to choose from, and we choose r=2 of them.
n = [0, 1, 2]
r = 2
The result of the function combos(n: [0, 1, 2], r: 2) would be:
result = [
[0, 0],
[0, 1],
[0, 2],
[1, 1],
[1, 2],
[2, 2]
]
// we don't need [1, 0], [2, 0] etc. because "order does not matter"
There are examples for doing this in many programming languages here: http://rosettacode.org/wiki/Combinations_with_repetitions
Here's the PHP example. It is one of the simplest and returns an array, which is what I want:
function combos($arr, $k) {
if ($k == 0) {
return array(array());
}
if (count($arr) == 0) {
return array();
}
$head = $arr[0];
$combos = array();
$subcombos = combos($arr, $k-1);
foreach ($subcombos as $subcombo) {
array_unshift($subcombo, $head);
$combos[] = $subcombo;
}
array_shift($arr);
$combos = array_merge($combos, combos($arr, $k));
return $combos;
}
Here's where I've got so far with porting the function to Swift:
func combos(var array: [Int], k: Int) -> AnyObject { // -> Array<Array<Int>> {
if k == 0 {
return [[]]
}
if array.isEmpty {
return []
}
let head = array[0]
var combos = [[]]
var subcombos: [Array<Int>] = combos(array, k-1) // error: '(#Ivalue [Int], $T5) -> $T6' is not identical to '[NSArray]'
for subcombo in subcombos {
var sub = subcombo
sub.insert(head, atIndex: 0)
combos.append(sub)
}
array.removeAtIndex(0)
combos += combos(array, k) // error: '(#Ivalue [Int], Int) -> $T5' is not identical to '[NSArray]'
return combos
}
Mostly I seem to be having problems with the type declarations of the various variables and whether these are mutable or immutable.
I've tried being more explicit and less explicit with the type declarations but all I'm managed to achieve are slightly different error messages.
I would be most grateful if someone would explain where I'm going wrong and why?
You can get rid of var sub = subcombo by writing the loop as
for subcombo in subcombos {
ret.append([head] + subcombo)
}
This can be further simplified using the map() function:
func combos<T>(var array: Array<T>, k: Int) -> Array<Array<T>> {
if k == 0 {
return [[]]
}
if array.isEmpty {
return []
}
let head = [array[0]]
let subcombos = combos(array, k: k - 1)
var ret = subcombos.map {head + $0}
array.removeAtIndex(0)
ret += combos(array, k: k)
return ret
}
Update for Swift 4:
func combos<T>(elements: ArraySlice<T>, k: Int) -> [[T]] {
if k == 0 {
return [[]]
}
guard let first = elements.first else {
return []
}
let head = [first]
let subcombos = combos(elements: elements, k: k - 1)
var ret = subcombos.map { head + $0 }
ret += combos(elements: elements.dropFirst(), k: k)
return ret
}
func combos<T>(elements: Array<T>, k: Int) -> [[T]] {
return combos(elements: ArraySlice(elements), k: k)
}
Now array slices are passed to the recursive calls to avoid
the creation of many temporary arrays.
Example:
print(combos(elements: [1, 2, 3], k: 2))
// [[1, 1], [1, 2], [1, 3], [2, 2], [2, 3], [3, 3]]
Your example gives combinations with repetition. For the record I have written a non-repetitive combination in Swift. I based it on the JavaScript version here: http://rosettacode.org/wiki/Combinations#JavaScript
I hope it helps others and if anyone can see improvement please do.. note this is my first attempt at Swift and was hoping for a neater way of doing the Swift equivalent of JavaScript slice.
func sliceArray(var arr: Array<Int>, x1: Int, x2: Int) -> Array<Int> {
var tt: Array<Int> = []
for var ii = x1; ii <= x2; ++ii {
tt.append(arr[ii])
}
return tt
}
func combinations(var arr: Array<Int>, k: Int) -> Array<Array<Int>> {
var i: Int
var subI : Int
var ret: Array<Array<Int>> = []
var sub: Array<Array<Int>> = []
var next: Array<Int> = []
for var i = 0; i < arr.count; ++i {
if(k == 1){
ret.append([arr[i]])
}else {
sub = combinations(sliceArray(arr, i + 1, arr.count - 1), k - 1)
for var subI = 0; subI < sub.count; ++subI {
next = sub[subI]
next.insert(arr[i], atIndex: 0)
ret.append(next)
}
}
}
return ret
}
var myCombinations = combinations([1,2,3,4],2)
Per the OP's request, here is a version which removes the custom Array slicing routine in favor of functionality in the standard library
// Calculate the unique combinations of elements in an array
// taken some number at a time when no element is allowed to repeat
func combinations<T>(source: [T], takenBy : Int) -> [[T]] {
if(source.count == takenBy) {
return [source]
}
if(source.isEmpty) {
return []
}
if(takenBy == 0) {
return []
}
if(takenBy == 1) {
return source.map { [$0] }
}
var result : [[T]] = []
let rest = Array(source.suffixFrom(1))
let sub_combos = combinations(rest, takenBy: takenBy - 1)
result += sub_combos.map { [source[0]] + $0 }
result += combinations(rest, takenBy: takenBy)
return result
}
var myCombinations = combinations([1,2,3,4], takenBy: 2)
// myCombinations = [[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]]
Updated #richgordonuk answer for Swift 4 which provides a non-repetitive combination:
func combinations<T>(source: [T], takenBy : Int) -> [[T]] {
if(source.count == takenBy) {
return [source]
}
if(source.isEmpty) {
return []
}
if(takenBy == 0) {
return []
}
if(takenBy == 1) {
return source.map { [$0] }
}
var result : [[T]] = []
let rest = Array(source.suffix(from: 1))
let subCombos = combinations(source: rest, takenBy: takenBy - 1)
result += subCombos.map { [source[0]] + $0 }
result += combinations(source: rest, takenBy: takenBy)
return result
}
Follow up on the existing answers extending RangeReplaceableCollection to support strings as well:
extension RangeReplaceableCollection {
func combinations(of n: Int) -> [SubSequence] {
guard n > 0 else { return [.init()] }
guard let first = first else { return [] }
return combinations(of: n - 1).map { CollectionOfOne(first) + $0 } + dropFirst().combinations(of: n)
}
func uniqueCombinations(of n: Int) -> [SubSequence] {
guard n > 0 else { return [.init()] }
guard let first = first else { return [] }
return dropFirst().uniqueCombinations(of: n - 1).map { CollectionOfOne(first) + $0 } + dropFirst().uniqueCombinations(of: n)
}
}
[1, 2, 3, 4, 5, 6].uniqueCombinations(of: 2) // [[1, 2], [1, 3], [1, 4], [1, 5], [1, 6], [2, 3], [2, 4], [2, 5], [2, 6], [3, 4], [3, 5], [3, 6], [4, 5], [4, 6], [5, 6]]
"abcdef".uniqueCombinations(of: 3) // ["abc", "abd", "abe", "abf", "acd", "ace", "acf", "ade", "adf", "aef", "bcd", "bce", "bcf", "bde", "bdf", "bef", "cde", "cdf", "cef", "def"]
You can use Apple's new library for this: https://github.com/apple/swift-algorithms/blob/main/Guides/Combinations.md
let numbers = [10, 20, 30, 40]
for combo in numbers.combinations(ofCount: 2) {
print(combo)
}
// [10, 20]
// [10, 30]
// [10, 40]
// [20, 30]
// [20, 40]
// [30, 40]
The main mistake I was making was to use a var named the same as my function:
combos += combos(array, k)
Which is why I was seeing an error on this line and the other line where my function was being called.
After fixing that the rest of the problems were easier to solve :)
In case it will help anyone here's my working function:
func combos<T>(var array: Array<T>, k: Int) -> Array<Array<T>> {
if k == 0 {
return [[]]
}
if array.isEmpty {
return []
}
let head = array[0]
var ret: Array<Array<T>> = []
var subcombos = combos(array, k - 1)
for subcombo in subcombos {
var sub = subcombo
sub.insert(head, atIndex: 0)
ret.append(sub)
}
array.removeAtIndex(0)
ret += combos(array, k)
return ret
}
If anyone can improve it I'd be happy
For example can anyone explain how to get rid of the line var sub = subcombo. i.e. how do I make subcombo mutable by default?
A functional take on the same algorithm:
func headTail<C: Collection>(_ c: C) -> (C.Element, C.SubSequence)? {
if c.isEmpty { return nil }
else { return (c.first!, c.dropFirst()) }
}
func combos<C: Collection>(_ c: C, by k: Int) -> [[C.Element]] {
if k <= 0 { return [[]] }
else if let (head, tail) = headTail(c) {
return combos(c, by: k-1).map { [head] + $0 } + combos(tail, by: k)
} else { return [] }
}
Or, as member functions over Collection:
extension Collection {
var headTail: (Element, SubSequence)? {
if isEmpty { return nil }
else { return (first!, dropFirst()) }
}
func combos(by k: Int) -> [[Element]] {
if k <= 0 { return [[]] }
else if let (head, tail) = headTail {
return combos(by: k-1).map { [head] + $0 } + tail.combos(by: k)
} else { return [] }
}
}

Resources