Randomize two arrays the same way Swift - swift

I know there is a new shuffle method with iOS 9
but I am wondering if there is anyway to shuffle two arrays the same way?
For example
[1,2,3,4] and [a,b,c,d]
shuffle
[3,4,1,2] and [c,d,a,b]

Using the shuffle() method from How do I shuffle an array in Swift? and the ideas from How can I sort multiple arrays based on the sorted order of another array
you can shuffle the array indices and then re-order both (or more)
arrays accordingly:
let a = [1, 2, 3, 4]
let b = ["a", "b", "c", "d"]
var shuffled_indices = a.indices.shuffle()
let shuffled_a = Array(PermutationGenerator(elements: a, indices: shuffled_indices))
let shuffled_b = Array(PermutationGenerator(elements: b, indices: shuffled_indices))
print(shuffled_a) // [3, 1, 2, 4]
print(shuffled_b) // ["c", "a", "b", "d"]
Update for Swift 3 (Xcode 8): PermutationGenerator does not
exist in Swift 3 anymore.
Using the shuffled() method
from Shuffle array swift 3 the same can be achieved with
var shuffled_indices = a.indices.shuffled()
let shuffled_a = shuffled_indices.map { a[$0] }
let shuffled_b = shuffled_indices.map { b[$0] }

Use a dictionary to store the values temporarily, shuffle the keys and then rebuild the other array by extracting the values from the dictionary.

I'm unaware of any built-in shuffle mechanism in Swift 2.0. Assuming this doesn't exist, I borrowed some code from here.
extension CollectionType where Index == Int {
/// Return a copy of `self` with its elements shuffled
func shuffle() -> [Generator.Element] {
var list = Array(self)
list.shuffleInPlace()
return list
}
}
extension MutableCollectionType where Index == Int {
/// Shuffle the elements of `self` in-place.
mutating func shuffleInPlace() {
// empty and single-element collections don't shuffle
if count < 2 { return }
for i in 0..<count - 1 {
let j = Int(arc4random_uniform(UInt32(count - i))) + i
guard i != j else { continue }
swap(&self[i], &self[j])
}
}
}
let shuffleOrder = [0,1,2,3]
let shuffled = shuffleOrder.shuffle()
var newArray1 = [String]()
var newArray2 = [String]()
let array1 = ["a", "b", "c", "d"]
let array2 = ["w", "x", "y", "z"]
shuffled.forEach() { index in
newArray1.append(array1[index])
newArray2.append(array2[index])
}
This solves the problem in a really straight forward way. It creates an array, shuffleOrder, that just has an index for each possible index in the starting arrays. It then shuffles these indices to create a random sampling order. Finally, it constructs two new arrays, based off of the starting arrays, sampling them with the shuffled values. While this doesn't mutate the original 2 arrays in place, it would be simple to modify this to do so.

Based upon Martin R's original answer, you could approach the problem using GameKit.
The answer is written in Swift4:
var arrayA = [1, 2, 3, 4]
var arrayB = ["a", "b", "c", "d"]
//Get The Indices Of The 1st Array
var shuffledIndices: [Int] = Array(arrayA.indices)
print("Shuffled Indices = \(shuffledIndices)")
//Shuffle These Using GameKit
shuffledIndices = GKRandomSource.sharedRandom().arrayByShufflingObjects(in: shuffledIndices) as! [Int]
//Map The Objects To The Shuffled Indices
arrayA = shuffledIndices.map { arrayA[$0] }
arrayB = shuffledIndices.map { arrayB[$0] }
//Log The Results
print("""
Array A = \(arrayA)
Array B = \(arrayB)
""")
Hope it helps ^_________^.

Related

Get all values into dictionary and create a String with a specific format

I have a dictionary with this structure:
a: [1,2]
b: [3,4]
c: [5,6]
and I need to return a string with this structure.
a,b,c\n1,3,5\n2,4,6
I solved the first part of the string. But to get the rest of the String. I try to iterate into my dictionary to get the first elements for each key in my dictionary and then get the rest for each value into the array.
Is there an easier way to get this?
Once you know what's the order of the keys (alpha ?), you can use this:
let dict: [String: [Int]] = ["a": [1,2], "b": [3, 4], "c": [5, 6]]
let keys = dict.keys.sorted() //Or do whatever you want here to get your target order
var matrix: [[String]] = []
keys.forEach {
guard let arrayAsInt = dict[$0] else { return }
let arrayAsString = arrayAsInt.map{ "\($0)" }
matrix.append( [$0] + arrayAsString)
}
print("Matrix: \(matrix)")
let transposed = matrix.transposed()
print("Transposed Matrix: \(transposed)")
let output = transposed.map { $0.joined(separator: ",")}.joined(separator: "\n")
print(output)
The outputs:
$>Matrix: [["a", "1", "2"], ["b", "3", "4"], ["c", "5", "6"]]
$>Transposed Matrix: [["a", "b", "c"], ["1", "3", "5"], ["2", "4", "6"]]
$>a,b,c
1,3,5
2,4,6
Obvisouly the "\n" might be invisible and be an actual new line
a,b,c
1,3,5
2,4,6
Being
a,b,c\n1,3,5\n2,4,6
What's the idea behind that? Create a matrix and use the transpose (it's used in maths with matrix, it's one of the basic modification of a matrix).
First transform the [String: [Int]] into a [[String]], where each element would be key followed by its values. I transformed it there as String for simpler code later.
Why doing that? Because the matrix value is easy to get from your initial dict. the transposed value is harder (not impossible) to get from dict but easier from matrix, and the transposed is quickly transformed into your format.
So my thinking was the reverse:
Get a structure from your output, then how to get it, it's a transpose, so I need to get the initial input as it, etc.
With the help of a code for Transpose Matrix (that accept String elements).
extension Collection where Self.Iterator.Element: RandomAccessCollection {
// PRECONDITION: `self` must be rectangular, i.e. every row has equal size.
func transposed() -> [[Self.Iterator.Element.Iterator.Element]] {
guard let firstRow = self.first else { return [] }
return firstRow.indices.map { index in
self.map{ $0[index] }
}
}
}
Any code (there a various) working ones, should the trick. I took it from here.
As pointed by #Leo Dabus, you can remove the Self.Iterator.Element
from the extension code (twice). I just wanted to it as such, not modifying the initial answer since it's not mind.
What you are looking for, besides composing the final string, is how to transpose a collection (this would work with collections of different sizes as well):
extension Sequence {
func max<T: Comparable>(_ predicate: (Element) -> T) -> Element? {
self.max(by: { predicate($0) < predicate($1) })
}
}
extension Collection where Element: RandomAccessCollection, Element.Indices == Range<Int> {
func transposed() -> [[Element.Element]] {
(0..<(max(\.count)?.count ?? .zero)).map {
index in compactMap { $0.indices ~= index ? $0[index] : nil }
}
}
}
let dict = ["a": [1,2,3],
"b": [4,5,6],
"c": [7,8,9]]
let sorted = dict.sorted(by: {$0.key < $1.key})
let result = sorted.map(\.key).joined(separator: ",") + "\n" +
sorted.map(\.value).transposed().map {
$0.map(String.init).joined(separator: ",")
}.joined(separator: "\n")
result // "a,b,c\n1,4,7\n2,5,8\n3,6,9"
A dictionary is an unordered collection so you need to sort it according to any specific key. Here I sort the dictionary according to the key if you don't care about an order you can just remove sort.
let dict: [String: Any] = ["a": [1,2], "b": [3,4], "c": [5,6]]
let sortedKey = dict.keys.sorted(by: <)
let key = sortedKey.joined(separator: ",")
var firstValues: [String] = []
var secondValues: [String] = []
sortedKey.forEach { (key) in
if let dictValue = dict[key] as? [Int],
let firstValue = dictValue.first,
let secondValue = dictValue.last {
firstValues.append("\(firstValue)")
secondValues.append("\(secondValue)")
}
}
let finalString = key + "\n" + firstValues.joined(separator: ",") + "\n" + secondValues.joined(separator: ",")
print(finalString) // "a,b,c\n1,3,5\n2,4,6"

How to check for certain values in one array without crashing the program [duplicate]

I have 2 Arrays. Say, array1 = [1,2,3,4,5] and array2 = [2,3]. How could I check in swift if array1 contains at least one item from array2?
You can do this by simply passing in your array2's contains function into your array1's contains function (or vice versa), as your elements are Equatable.
let array1 = [2, 3, 4, 5]
let array2 = [20, 15, 2, 7]
// this is just shorthand for array1.contains(where: { array2.contains($0) })
if array1.contains(where: array2.contains) {
print("Array 1 and array 2 share at least one common element")
} else {
print("Array 1 doesn't contains any elements from array 2")
}
This works by looping through array 1's elements. For each element, it will then loop through array 2 to check if it exists in that array. If it finds that element, it will break and return true – else false.
This works because there are actually two flavours of contains. One takes a closure in order to check each element against a custom predicate, and the other just compares an element directly. In this example, array1 is using the closure version, and array2 is using the element version. And that is the reason you can pass a contains function into another contains function.
Although, as correctly pointed out by #AMomchilov, the above algorithm is O(n2). A good set intersection algorithm is O(n), as element lookup is O(1). Therefore if your code is performance critical, you should definitely use sets to do this (if your elements are Hashable), as shown by #simpleBob.
Although if you want to take advantage of the early exit that contains gives you, you'll want to do something like this:
extension Sequence where Iterator.Element : Hashable {
func intersects<S : Sequence>(with sequence: S) -> Bool
where S.Iterator.Element == Iterator.Element
{
let sequenceSet = Set(sequence)
return self.contains(where: sequenceSet.contains)
}
}
if array1.intersects(with: array2) {
print("Array 1 and array 2 share at least one common element")
} else {
print("Array 1 doesn't contains any elements from array 2")
}
This works much the same as the using the array's contains method – with the significant difference of the fact that the arraySet.contains method is now O(1). Therefore the entire method will now run at O(n) (where n is the greater length of the two sequences), with the possibility of exiting early.
With Swift 5, you can use one of the following paths in order to find if two arrays have common elements or not.
#1. Using Set isDisjoint(with:) method
Set has a method called isDisjoint(with:). isDisjoint(with:) has the following declaration:
func isDisjoint(with other: Set<Element>) -> Bool
Returns a Boolean value that indicates whether the set has no members in common with the given sequence.
In order to test if two arrays have no common elements, you can use the Playground sample code below that implements isDisjoint(with:):
let array1 = [1, 3, 6, 18, 24]
let array2 = [50, 100, 200]
let hasNoCommonElement = Set(array1).isDisjoint(with: array2)
print(hasNoCommonElement) // prints: true
#2. Using Set intersection(_:) method
Set has a method called intersection(_:). intersection(_:) has the following declaration:
func intersection<S>(_ other: S) -> Set<Element> where Element == S.Element, S : Sequence
Returns a new set with the elements that are common to both this set and the given sequence.
In order to test if two arrays have no common elements or one or more common elements, you can use the Playground sample code below that implements intersection(_:):
let array1 = [1, 3, 6, 18, 24]
let array2 = [2, 3, 18]
let intersection = Set(array1).intersection(array2)
print(intersection) // prints: [18, 3]
let hasCommonElement = !intersection.isEmpty
print(hasCommonElement) // prints: true
An alternative way would be using Sets:
let array1 = [1,2,3,4,5]
let array2 = [2,3]
let set1 = Set(array1)
let intersect = set1.intersect(array2)
if !intersect.isEmpty {
// do something with the intersecting elements
}
Swift 5
Just make an extension
public extension Sequence where Element: Equatable {
func contains(anyOf sequence: [Element]) -> Bool {
return self.filter { sequence.contains($0) }.count > 0
}
}
Use:
let someArray = ["one", "two", "three"]
let string = "onE, Cat, dog"
let intersects = string
.lowercased()
.replacingOccurrences(of: " ", with: "")
.components(separatedBy: ",")
.contains(anyOf: someArray)
print(intersects) // true
let a1 = [1, 2, 3]
let a2 = [2, 3, 4]
Option 1
a2.filter { a1.contains($0) }.count > 1
Option 2
a2.reduce(false, combine: { $0 || a1.contains($1) })
Hope this helps.
//
// Array+CommonElements.swift
//
import Foundation
public extension Array where Element: Hashable {
func set() -> Set<Array.Element> {
return Set(self)
}
func isSubset(of array: Array) -> Bool {
self.set().isSubset(of: array.set())
}
func isSuperset(of array: Array) -> Bool {
self.set().isSuperset(of: array.set())
}
func commonElements(between array: Array) -> Array {
let intersection = self.set().intersection(array.set())
return intersection.map({ $0 })
}
func hasCommonElements(with array: Array) -> Bool {
return self.commonElements(between: array).count >= 1 ? true : false
}
}

Most efficient way to flatten an array of dictionaries containing arrays

Let's say I have an array of dictionaries, and each contains an array of letters. Like this:
let dicts = [["letters" : ["a","b","c"]],
["letters" : ["d","e","f"]]]
What is the most efficient way to create a flattened array of all the letters from all the dictionaries?
You can use reduce(_:_:) for that.
let array = dicts.reduce([]) { $0 + ($1["letters"] ?? []) }
print(array) // ["a","b","c","d","e","f"]
Edit: As #Hamish suggested a link in comment simplest solution is having less time, so if you are having large amount of data then you can use forEach closure with array.
var result = [String]()
dicts.forEach {
result.append(contentsOf: $0["letters"] ?? [])
}
You can use flatMap() to map each dictionary to the corresponding
letters array and join the results:
let dicts = [["letters" : ["a","b","c"]],
["letters" : ["d","e","f"]]]
let letters = Array(dicts.flatMap { $0["letters"] }.joined())
print(letters) // ["a", "b", "c", "d", "e", "f"]
This is effective insofar as no additional intermediate arrays are
created during the join. As #Hamish pointed out below, the
intermediate [[String]] can be avoided with
let letters = Array(dicts.lazy.flatMap { $0["letters"] }.joined())
You can use any one of these. You don't need to specify the key name of the dictionary.
Solution 1
let array = dicts.reduce([]) { $0 + ($1.values.reduce([]) { $0 + $1 }) }
print(array) // ["a","b","c","d","e","f"]
Solution 2
let array = dicts.flatMap { $0.values.joined() }
print(array) // ["a","b","c","d","e","f"]

How to compare to arrays whether the objects of one array are there in the other or not

let say we have:
var a = ["z", "x"]
var b = ["z", "x", "c", "d"]
now i want to check whether objects of array a are there in array b or not..
thanks for your time..
The simplest way is to use map in combination with contains:
let matches = map(a) { return contains(b, $0) }
contains checks to see if an item is in a collection
map is used to convert one array into another array with an element-by-element mapping.
As pointed out by #marcos, there are similar variants to answer the any and all questions as well
let any = a.reduce(false) { acc, item in return acc || contains(b, item) }
let all = a.reduce(true) { acc, item in return acc && contains(b, item) }
Just use the map:
var a = [1, 7, 5, 2]
var b = [1, 2, 3, 4]
let d = a.map { b.contains($0) }
print(d) // [true, false, false, true]
I think this following code snippet wil work:
var booleanArray = [Bool]()
for x in a{
if !contains(b, x){
booleanArray.append(false)
}
else{
booleanArray.append(true)
}
You can use a for in loop and then use the array contains function:
Swift 2.0
var resultsArray : [Bool] = [Bool]()
let a = ["a","b","c"]
let b = ["a","c"]
for letter in a {
if b.contains(letter) == true {
// Letter exists in a array
resultsArray.append(true)
} else {
// Letter does not exist in a array
resultsArray.append(false)
}
}
Swift 1.2
Replace b.contains(letter) with:
contains(b, letter)
You could convert the arrays to Set to use the intersect method
let a = ["a","c"]
let b = ["a","b","c","d"]
let aSet = Set(a)
let bSet = Set(b)
let intersection = aSet.intersect(bSet) // ["a", "c"]

How to remove an element of a given value from an array in Swift

I want to remove all elements of value x from an array that contains x, y and z elements
let arr = ['a', 'b', 'c', 'b']
How can I remove all elements of value 'b' from arr?
A filter:
let farray = arr.filter {$0 != "b"}
If you need to modify initial array, you can use the function removeAll(where:) that is available in Swift 4.2/Xcode 10:
var arr = ["a", "b", "c", "b"]
arr.removeAll(where: { $0 == "b" })
print(arr) // output is ["a", "c"]
However, if you are using Xcode 9 you can find this function in Xcode9to10Preparation (this library provides implementations of some new functions from Xcode 10).
var array : [String]
array = ["one","two","one"]
let itemToRemove = "one"
while array.contains(itemToRemove) {
if let itemToRemoveIndex = array.index(of: itemToRemove) {
array.remove(at: itemToRemoveIndex)
}
}
print(array)
Works on Swift 3.0.
EDITED according to comments:
I like this approach:
var arr = ["a", "b", "c", "b"]
while let idx = arr.index(of:"b") {
arr.remove(at: idx)
}
Original answer (before editing):
let arr = ['a', 'b', 'c', 'b']
if let idx = arr.index(of:"b") {
arr.remove(at: idx)
}
In Swift 3 I simply do:
arr = arr.filter { $0 != "a" }
.filter, .sort and .map are great for saving time and solve lots of problems with little code.
This article has good examples and explain the differences and how they work: https://useyourloaf.com/blog/swift-guide-to-map-filter-reduce/
If you have more than one element to remove, thanks to first answer.
var mainArray = ["a", "b", "qw", "qe"]
let thingsToRemoveArray = ["qw", "b"]
for k in thingsToRemoveArray {
mainArray = mainArray.filter {$0 != k}
}
A general approach is to exploit first class procedures. (However, this approach is much more powerful than what is required for your question.) To illustrate, say you want to avoid "Justin" repeatedly in many collections.
let avoidJustin = notEqualTester ("Justin")
let arrayOfUsers = // ...
arrayOfUsers.filter (avoidJustin)
let arrayOfFriends = // ...
arrayOfFriends.filter (avoidJustin)
With this, you avoid repeatedly creating a closure each time you want to avoid Justin. Here is notEqualTester which, given a that, returns a function of this that returns this != that.
func notEqualTester<T: Equatable> (that:T) -> ((this:T) -> Bool) {
return { (this:T) -> Bool in return this != that }
}
The returned closure for this captures the value for that - which can be useful when that is no longer available.
If you only have one element of that value, this would probably be the easiest.
arr.remove(at: arr.index(of: ‘b’))