Swift - array of strings to multiple subarrays with constant number of strings - arrays

Let's say I have this array of strings:
let Vehicles = ["Aeroplane", "Bicycle", "CarVehicle", "Lorry", "Motorbike", "Scooter", "Ship", "Train"]
What I want is this result:
let resultArray = [["Aeroplane", "Bicycle", "CarVehicle", "Lorry"], ["Motorbike", "Scooter", "Ship", "Train"]]
I know I could do this by for but I want to use Higher Order functions in Swift. I mean functions like map, reduce, filter. I think it's possible to do this way and it could be better. Can anyone help me with this? Thanks

A possible solution with map() and stride():
let vehicles = ["Aeroplane", "Bicycle", "CarVehicle", "Lorry", "Motorbike", "Scooter", "Ship", "Train"]
let each = 4
let resultArray = map(stride(from: 0, to: vehicles.count, by: each)) {
vehicles[$0 ..< advance($0, each, vehicles.count)]
}
println(resultArray)
// [[Aeroplane, Bicycle, CarVehicle, Lorry], [Motorbike, Scooter, Ship, Train]]
The usage of advance() in the closure guarantees that the code
works even if the number of array elements is not a multiple of 4
(and the last subarray in the result will then be shorter.)
You can simplify it to
let resultArray = map(stride(from: 0, to: vehicles.count, by: each)) {
vehicles[$0 ..< $0 + each]
}
if you know that the number of array elements is a multiple of 4.
Strictly speaking the elements of resultArray are not arrays
but array slices. In many cases that does not matter, otherwise you
can replace it by
let resultArray = map(stride(from: 0, to: vehicles.count, by: each)) {
Array(vehicles[$0 ..< advance($0, each, vehicles.count)])
}

Related

How to enumerate a zip array in swift 4 to get index for each object

I need to enumerate a zipped array to check index for each object from array. Below is my code please help me out.
for((index1, index2),(value1, value2)) in zip(array1, array2).enumerated() {
// INDEX OF OBJECT
}
Once you zip the array it is having only one index. Just use it as normal enumerations.
let arr1 = ["1","2","3"]
let arr2 = ["A","B"]
let arr3 = zip(arr1, arr2)
print(arr3)
for (index, (x,y)) in arr3.enumerated() {
print(index, y)
}
enjoy!
You can make use of enumerated to know index of value like this way.
func zipArray() {
let words = ["one", "two", "three", "four"]
let numbers = 1...4
let zipArray = Array(zip(words, numbers))
print(zipArray)
for (index, tuple) in zipArray.enumerated() {
print("Index : \(index): Word :\(tuple.0) Number : \(tuple.1)")
}
}
Once zip perform on two array then it returns another array with pair of inputs.
So you can perform enumerated operation on zipped array using above code and get index and value as tuple. Then for individual element of tuple index would be same.
There may be better way. But this will fulfill your purpose of index.

Swift 3 Extracting similar objects

I have an array of arrays containg int, so let pairs:[[Int]].
I am looking for an elegant way to extract similar elements.
For example my pairs variable could contain something like: [ [1,2], [4,6], [1,2] ]
I would like to extract any array that occurs more than once like [1,2].
In the example [ [1,2], [4,6], [1,2], [3,7], [4,6] ] I would like to extract both [1,2] and [4,6].
This seemed trivial at first, but every go I had at it became very cumbersome with many "helper arrays" and nested "for loops". There surely is a simpler way in Swift, right?
Thanks
Here the way using just one helper Dictionary and one loop:
let pairs = [[1,2], [4,6], [1,2]] // The original array
var pairCount = [NSArray : Int]() // This is helper dictionary. The key is the array, the value is - how much time it appears in the source array. I use NSArray because swift array does not conform to Hashable protocol.
var newPairs = [[Int]]()
for pair in pairs { // Iterate over our pairs
var count: Int = pairCount[pair as NSArray] ?? 0 // If we already have a pair in our dictionary get count of it in array otherwise het 0
count += 1 // increase counter
pairCount[pair as NSArray] = count // save in dictionary
}
let pairsWithCountMoreThanOne = pairCount.flatMap({$1 > 1 ? $0 : nil}) // here we get the result
for pair in pairsWithCountMoreThanOne { // Just print it
print("\(pair)")
}
This code may not be memory efficient for large arrays or large objects but it is really trivial.
Please check the below :
let pairs = [ [1,2], [4,6], [1,2], [3,7], [4,6], [3,5], [4,6] ]
var repeats = [[Int]]()
pairs.forEach { (i) in
let count = (pairs.filter{ return $0 == i }).count
if count > 1 {
if !repeats.contains(where: { (pair) -> Bool in
return pair == i
}) {
repeats.append(i)
}
}
}
print(repeats) // Output is : [[1, 2], [4, 6]]

Randomly select 5 elements from an array list without repeating an element

I am currently trying to make an app for iOS but I can't get some simple code down. Basically I need to randomly select 5 elements from an array list without repeating an element. I have a rough draft, but it only displays one element.
Here is my code:
let array1 = ["salmon", "turkey", "salad", "curry", "sushi", "pizza"]
let randomIndex1 = Int(arc4random_uniform(UInt32(array1.count)))
print(array1[randomIndex1])
You can do it like this:
let array1 = ["salmon", "turkey", "salad", "curry", "sushi", "pizza", "curry", "sushi", "pizza"]
var resultSet = Set<String>()
while resultSet.count < 5 {
let randomIndex = Int(arc4random_uniform(UInt32(array1.count)))
resultSet.insert(array1[randomIndex])
}
let resultArray = Array(resultSet)
print(resultArray)
A set can contain unique elements only, so it can't have the same element more than once.
I created an empty set, then as long as the array contains less than 5 elements (the number you chose), I iterated and added a random element to the set.
In the last step, we need to convert the set to an array to get the array that you want.
SWIFT 5.1
This has been answered, but I dare to come with a more simple solution.
If you take your array and convert it into a Set you will remove any duplicated items and end up with a set of unique items in no particular order since the nature of a Set is unordered.
https://developer.apple.com/documentation/swift/set
If you then convert it back to an array and pick 5 elements you will end up with an array of random items.
But it's just 1 line ;)
Example:
var original = ["A","B","C","C","C","D","E","F","G","H"]
let random = Array(Set(original)).prefix(5)
Example print:
["B", "G", "H", "E", "C"]
The only requirement is your objects must conform to the Hashable protocol. Most standard Swift types do, otherwise, it's often simple to make your own types conform.
https://developer.apple.com/documentation/swift/hashable
If you don't care about changing the original array, the following code will put the picked elements at the end of the array and then it will return the last part of the array as a slice.
This is useful if you don't care about changing the original array, the advantage is that it doesn't use extra memory, and you can call it several times on the same array.
extension Array {
mutating func takeRandomly(numberOfElements n: Int) -> ArraySlice<Element> {
assert(n <= self.count)
for i in stride(from: self.count - 1, to: self.count - n - 1, by: -1) {
let randomIndex = Int(arc4random_uniform(UInt32(i + 1)))
self.swapAt(i, randomIndex)
}
return self.suffix(n)
}
}
Example:
var array = [1,2,3,4]
let a1 = array.takeRandomly(numberOfElements: 2)
let a2 = array.takeRandomly(numberOfElements: 2)
swift-algorithms now includes an extension to Sequence called randomSample.
import Algorithm
var array1 = ["salmon", "turkey", "salad", "curry", "sushi", "pizza"]
array1.randomSample(count: 5)
Just my ยข2:
Moe Abdul-Hameed's solution has one theoretical drawback: if you roll the same randomIndex in every iteration, the while loop will never exit. It's very unlike tho.
Another approach is to create mutable copy of original array and then exclude picked items:
var source = array1
var dest = [String]()
for _ in 1...5 {
let randomIndex = Int(arc4random_uniform(UInt32(source.count)))
dest.append(source[randomIndex])
source.remove(at: randomIndex)
}
print(dest)
var array1 = ["salmon", "turkey", "salad", "curry", "sushi", "pizza"]
while array1.count > 0 {
// random key from array
let arrayKey = Int(arc4random_uniform(UInt32(array1.count)))
// your random number
let randNum = array1[arrayKey]
// make sure the number ins't repeated
array1.removeAtIndex(arrayKey)
}
By removing your picked value from array, prevent's from duplicates to be picked

How to obtain three arrays containing three objects or less?

I want to obtain three arrays containing three objects or less. I have a class named Product and an array who have 9 products or less downloaded form Firebase, so I want to generate three arrays, each one will have three different products in order. This is what I have:
var products = [Product]()
products = [product1, product2, product3, product4, product5, product6, product7, product8, product9]
And this is what I want to obtain:
array1 = [product1, product2, product3]
array2 = [product4, product5, product6]
array3 = [product7, product8, product9]
In some cases the array named product will have a number of products less than 9 so I have to create those arrays automatically with 3 or less products.
I'm doing this because my code have to generate an array that has three arrays inside, each one with three products or less to show in a collection view inside a tableview [[Product]].
You can slice an array by using the [] operator with a range. Note that this returns a view into the original array, so if you want a copy you pass the slice to Array() to create a new copy:
let numbers:[Int] = stride(from: 1, to: 10, by: 1).map{$0}
print(numbers.count)
let first = Array(numbers[0...2])
let second = Array(numbers[3...5])
let third = Array(numbers[6...8])
print(first, second, third)
To convert an array of arbitrary length into a number of arrays with 3 elements or less:
let numbers:[Int] = stride(from: 1, to: 14, by: 1).map{$0}
var bins: [[Int]] = []
for index in stride(from: 0, to: numbers.count, by: 3) {
let endIndex = min(index + 2, numbers.count - 1)
bins.append(Array(numbers[index...endIndex]))
}
print(bins)
Gustavo expects the extra variables to contain empty arrays when there are fewer than 6 or 3 products.
this should do it:
let productsBy3 = (0..<3).map{ i in products.indices.filter{$0/3==i}.map{products[$0]}}

swift getting an array from something like array[0..<10]

I want to get a range of objects from an array. Something like this:
var array = [1,3,9,6,3,4,7,4,9]
var newArray = array[1...3] //[3,9,6]
The above would access elements from index 1 to 3.
Also this:
newArray = array[1,5,3] // [3,4,6] would be cool
This would retrieve elements from index 1, 5 and 3 respectively.
That last example can be achieved using PermutationGenerator:
let array = [1,3,9,6,3,4,7,4,9]
let perms = PermutationGenerator(elements: array, indices: [1,5,3])
// perms is now a sequence of the values in array at indices 1, 5 and 3:
for x in perms {
// iterate over x = 3, 4 and 6
}
If you really need an array (just the sequence may be enough for your purposes) you can pass it into Array's init method that takes a sequence:
let newArray = Array(perms)
// newArray is now [3, 4, 6]
For your first example - with arrays, that will work as-is. But it looks from your comments like you're trying it with strings as well. Strings in Swift are not random-access (for reasons relating to unicode). So you can't use integers, they have an String-specific bidirectional index type:
let s = "Hello, I must be going"
if let i = find(s, "I") {
// prints "I must be going"
println(s[i..<s.endIndex])
}
This works :
var n = 4
var newArray = array[0..<n]
In any case in
Slicing Arrays in Swift you'll find a very nice sample of the Python slice using a extension to Arrays in Swift.

Resources