Get a set number of random items from array [duplicate] - arrays

This question already has answers here:
Get random elements from array in Swift
(6 answers)
Closed 5 years ago.
I want to write an extension which allows me to put in an array and return a set number of elements from that array with no repeated items. How would I do that? This is what I have so far but it is not perfect. It does not consider duplicates and it does not seem like the best way for this to be done. I was thinking it might make sense to use a set for duplicates.
extension Array {
func randomElement(numberOfItems:Int) -> [Element] {
var finalReturn = Array()
for i in 0..<numberOfItems {
finalReturn.append(self[Int(arc4random_uniform(UInt32(self.count)))])
}
return finalReturn
}
}
usage should be like this.
let selected = allData.randomElement(numberOfItems: 10)

Here is one way to do it:
extension Array {
func randomElements(number: Int) -> [Element] {
guard number > 0 else { return [Element]() }
var remaining = self
var chosen = [Element]()
for _ in 0 ..< number {
guard !remaining.isEmpty else { break }
let randomIndex = Int(arc4random_uniform(UInt32(remaining.count)))
chosen.append(remaining[randomIndex])
remaining.remove(at: randomIndex)
}
return chosen
}
}
Sample:
let testArray = [1, 2, 3, 4, 5, 6, 7, 8, 9]
let randomThree = testArray.randomElements(number: 3)
// randomThree is [1, 5, 4]
Depending on your use case, you may want to change the behavior when the number of elements requested is greater than the number of elements in the array.
In my sample above, if this is the case, I return the maximum number of elements possible (the number of elements in the original array). Alternatively, you could give an error or return nil.

Related

Adding non-repeating random numbers to an array [duplicate]

This question already has answers here:
How do I shuffle an array in Swift?
(25 answers)
Closed 5 years ago.
Hi I am trying to create an array from non-repeating random numbers.
I wrote below code numberOfAnimals which is 10 currently shows how many numbers will be in the array. When I run this in playground I get
"[6, 5, 1, 4, 7, 0]\n" as output of print statement so in total 6 numbers instead of 10. To avoid it I am decreasing value of i in if statement in case random number exists in the array already, in this case for loop needs an additional loop to reach 10 but still it does not work.
Can you please check where is the mistake or give me another code suggestion that can work ?
import UIKit
var array = [Int]()
var max : Int = 10
var numberOfAnimals : Int = 10
for var i in 0..<numberOfAnimals {
let randomNumber : Int = Int(arc4random_uniform(UInt32(max)))
if array.contains(randomNumber) {
i = i - 1
} else {
array.append(randomNumber)
}
}
print(array)
Why not have a while loop that continues until you have ten numbers in your array.
while array.count < 10 {
...
if !array.contains(randomNumber) {
array.append(randomNumber)
}
}
If you repeat your loop until every number has been hit, the worst case runtime of your algorithm is infinite. As far is I understand your problem, you have an array with fixed size and fixed elements, which you want to be shuffled, so why not do this? I found a nice algorithm in another thread:
extension MutableCollection {
mutating func shuffle() {
let c = count
guard c > 1 else { return }
for (firstUnshuffled, unshuffledCount) in zip(indices, stride(from: c, to: 1, by: -1)) {
let d: IndexDistance = numericCast(arc4random_uniform(numericCast(unshuffledCount)))
let i = index(firstUnshuffled, offsetBy: d)
swapAt(firstUnshuffled, i)
}
}
}
let numberOfAnimals = [1,2,3,4,5,6,7,8,9,10]
array.shuffle()
print(array)
I'd suggest a slight modification of #Joakim Danielson 's answer if you can afford the extra space and implement a set - if an array isn't of vital importance you may want to consider a set for your implementation as well, just $0.02
var array = [Int]()
var seen = Set<Int>()
let max = 10
while array.count < max {
let number = Int(arc4random_uniform(UInt32(max)))
if seen.contains(number) {
continue
}
array.append(number)
seen.insert(number)
}
print(array)

Swift compare 2 array of type [int] without guaranteed order [duplicate]

This question already has answers here:
Find whether two Swift Arrays contain the same elements
(3 answers)
Closed 5 years ago.
I make app for performing tests. For check for valid answer i have array of correct answers, of type [Int]. It may be, for example, [1, 5, 10].
Also i have array of questions id's that user entered, also [Int] type.
Now i have to somehow compare that arrays. But, [1,5,10] should be equal to [10,1,5] and [5,1,10], etc., because order is not guaranteed. How to achieve that?
If you want a function that returns true if an array contains all elements of your array and only those elements, you can use below Array Extension. If the length of the two arrays isn't the same or if one of the elements in your array isn't present in the other array, the function returns false, otherwise it returns true.
let array1 = [1,5,10]
let array2 = [5,10,1]
let array3 = [10,1,5]
let otherArray = [2,1,5,10]
let anotherArray = [2,3,5]
extension Array where Element == Int {
func isUnorderedEqual(to array: [Element])->Bool{
if self.count == array.count {
for element in self {
if array.index(of: element) == nil {
return false
}
}
return true
} else {
return false
}
}
}
print(array1.isUnorderedEqual(to: array2)) //true
print(array1.isUnorderedEqual(to: array3)) //true
print(array1.isUnorderedEqual(to: otherArray)) //false
print(array1.isUnorderedEqual(to: anotherArray)) //false
You can filter for any correct answers not in the list of answers and look if any such item exists.
let correctAnswers: [Int] = [1, 5, 10]
func validate(answers: [Int]) -> Bool {
return answers.filter({ answer in !correctAnswers.contains(answer) }).isEmpty
}
validate(answers: [10, 1, 5])

Changing the size of an array inside of in for-in loop

I was presented this problem where I need to find matching numbers in a given array (socks) and print out how many socks found inside that array. Here is my code:
let numberOfSocks = 9
let socksArray = [10, 20, 20, 10, 10, 30, 50, 10]
func findSocks(numberOfSocks: Int, array: [Int]) {
var arr = array
var uniqueSocks = Array(Set(array))
var matchedPairs = 0
var sockCounter = 0
for i in 0..<uniqueSocks.count { // After the search, remove the element at index
sockCounter = 0
for j in 0..<arr.count {
if uniqueSocks[i] == arr[j] {
sockCounter += 1
if sockCounter % 2 == 0 {
matchedPairs += 1
sockCounter = 0
}
}
}
}
print(matchedPairs)
}
findSocks(numberOfSocks: numberOfSocks, array: socksArray)
Firstly, I have removed all the duplicates in the array so it gives me a list unique socks that I need to search for. However, I wanted to optimize this algorithm by remove the sock that I have already searched for, I have tried arr.remove(at:) but It gave me an out of bound, I have a feeling that arr.count is not updated correctly. Any help is welcome, thanks!
I think that you're overthinking the problem, focusing on small details rather than the big picture. What you want to end up with is essentially a dictionary where the keys are the unique values in the array, and the values are the number of times those values appear in the array. So start with your dictionary:
var counts = [Int : Int]()
There's no need for your arr and numberOfSocks variables. Instead of the latter, just use socksArray.count, which clearly will always be in sync with the true size of the array.
Now loop through your socks. For each sock value, increment its count in the counts dictionary or, if it's not already in the dictionary, add it and give it a count of 1.
for sock in socks {
if !counts.contains(sock) {
counts[sock] = 1
} else {
counts[sock] = counts[sock] + 1
}
}
There are more concise ways to do this, but I think that this one is the easiest to read.

How to Remove Every Other Element in an Array in Swift?

So say I have an array:
var stringArray = ["a","b","c","d","e","f","g","h","i","j"]
Now, how do I delete "a", "c", "e", "g", and "i" (all the even number indexes from the array)?
Thanks!
Instead of using C-style for-loops (which are set to be deprecated in an upcoming version of Swift), you could accomplish this using strides:
var result = [String]()
for i in stride(from: 1, through: stringArray.count - 1, by: 2) {
result.append(stringArray[i])
}
Or for an even more functional solution,
let result = stride(from: 1, to: stringArray.count - 1, by: 2).map { stringArray[$0] }
Traditional
var filteredArray = []
for var i = 1; i < stringArray.count; i = i + 2 {
filteredArray.append(stringArray[i])
}
Functional alternative
var result = stringArray.enumerate().filter({ index, _ in
index % 2 != 0
}).map { $0.1 }
enumerate takes a array of elements and returns an array of tuples where each tuple is an index-array pair (e.g. (.0 3, .1 "d")). We then remove the elements that are odd using the modulus operator. Finally, we convert the tuple array back to a normal array using map. HTH
There are a bunch of different ways to accomplish this, but here are a couple that I found interesting:
Using flatMap() on indices:
let result: [String] = stringArray.indices.flatMap {
if $0 % 2 != 0 { return stringArray[$0] }
else { return nil }
}
Note: result needs to be defined as a [String] otherwise the compiler doesn't know which version of flatMap() to use.
Or, if you want to modify the original array in place:
stringArray.indices.reverse().forEach {
if $0 % 2 == 0 { stringArray.removeAtIndex($0) }
}
In this case you have to call reverse() on indices first so that they're enumerated in reverse order. Otherwise by the time you get to the end of the array you'll be attempting to remove an index that doesn't exist anymore.
Swift 4.2
A function accepting generics and producing reduced result
func stripElements<T>(in array:[T]) -> [T] {
return array.enumerated().filter { (arg0) -> Bool in
let (offset, _) = arg0
return offset % 2 != 0
}.map { $0.element }
}

Easy way to check an array for contiguous three or more numbers in Swift

Is there any easy way to check if an array contains contiguous value of three or more? e.g. [4, 2, 1, 1, 1, 7, 4, 4, 4, 4] contains two contiguous sequence of 1 and 4. To check I wish to give 1 and minimum allowed conjugation, in this case 2, it will just return true. Thanks.
We can start out by making use of a neat extension to SequenceType by user #oisdk in his answer in the following thread:
How to find same value(duplicate) in an int array in order?
The extension groups successive elements in tuples (value, numberOfSuccessions):
/* from SO user #oisdk:s answer in Q&A:
https://stackoverflow.com/a/35325141/4573247 */
extension SequenceType where Generator.Element: Equatable {
func group() -> [(Generator.Element, Int)] {
var res: [(Generator.Element, Int)] = []
for el in self {
if res.last?.0 == el {
res[res.endIndex-1].1 += 1
} else {
res.append((el,1))
}
}
return res
}
}
Using this, we can swiftly write another extension for checking if---for a given array---a contiguous sequence (for some minimum number of successions/repeats) exists for a given number:
extension SequenceType where Generator.Element == Int {
func containsContiguousValue(value: Int, forMinimumRepeats rep: Int) -> Bool {
return !self
.group()
.contains{ (val, count) in count >= rep && val == value }
}
}
Used as follows
/* Example usage */
let array = [4, 2, 1, 1, 1, 7, 4, 4, 4, 4]
array.containsContiguousValue(1, forMinimumRepeats: 3) // true
array.containsContiguousValue(1, forMinimumRepeats: 4) // false
array.containsContiguousValue(4, forMinimumRepeats: 4) // true
array.containsContiguousValue(2, forMinimumRepeats: 3) // false
I think the simplest possible way is with the help of the reduce function. If you want you can extend the data structures, but I am not quite a fan of that. So here is a simple solution to your example
// example array
let a = [4, 2, 1, 1, 1, 7, 4, 4, 4, 4]
let minRepeats = 3 // desired min repeats
let elementToCheck = 4 // element to check
let m = a.reduce(0) { (initial: Int, el: Int) -> Int in
if initial >= minRepeats {
return initial
} else {
return el == elementToCheck ? initial + 1 : 0
}
}
// if m == minRepeats the check is positive, if m < minRepeats the check is negative
// let check = a.reduce(0){...} == minRepeats gives you the right result
// Thanks to user3441734 for the correction
The answers above were helpful but not quite as generic as I needed. Also, they are a little outdated, so for those who come across this requirement, here's a generic reusable Swift 4.2 implementation:
An extension on any Collection that returns an array of ranges representing the indices of consecutive elements in a collection matching a given predicate.
https://gist.github.com/shaps80/8ec24f82ad1e54d42709277ec2af93a3

Resources