I need to find the most common (modal) elements in an array.
The simplest way I could think of was to set variables for each unique element, and assign a count variable for each one, which increases every time it is recorded in a for loop which runs through the array.
Unfortunately the size of the array is unknown and will be very large, so this method is useless.
I have come across a similar question in Objective-C that uses an NSCountedSet method to rank the array elements. Unfortunately I am very new to programming, and could only translate the first line into Swift.
The suggested method is as follows:
var yourArray: NSArray! // My swift translation
NSCountedSet *set = [[NSCountedSet alloc] initWithArray:yourArray];
NSMutableDictionary *dict=[NSMutableDictionary new];
for (id obj in set) {
[dict setObject:[NSNumber numberWithInteger:[set countForObject:obj]]
forKey:obj]; //key is date
}
NSLog(#"Dict : %#", dict);
NSMutableArray *top3=[[NSMutableArray alloc]initWithCapacity:3];
//which dict obj is = max
if (dict.count>=3) {
while (top3.count<3) {
NSInteger max = [[[dict allValues] valueForKeyPath:#"#max.intValue"] intValue];
for (id obj in set) {
if (max == [dict[obj] integerValue]) {
NSLog(#"--> %#",obj);
[top3 addObject:obj];
[dict removeObjectForKey:obj];
}
}
}
}
NSLog(#"top 3 = %#", top3);
In my program I will need to find the top five place names in an array.
edit: now with Swift 2.0 below
Not the most efficient of solutions but a simple one:
let a = [1,1,2,3,1,7,4,6,7,2]
var frequency: [Int:Int] = [:]
for x in a {
// set frequency to the current count of this element + 1
frequency[x] = (frequency[x] ?? 0) + 1
}
let descending = sorted(frequency) { $0.1 > $1.1 }
descending now consists of an array of pairs: the value and the frequency,
sorted most frequent first. So the “top 5” would be the first 5 entries
(assuming there were 5 or more distinct values). It shouldn't matter how big the source array is.
Here's a generic function version that would work on any sequence:
func frequencies
<S: SequenceType where S.Generator.Element: Hashable>
(source: S) -> [(S.Generator.Element,Int)] {
var frequency: [S.Generator.Element:Int] = [:]
for x in source {
frequency[x] = (frequency[x] ?? 0) + 1
}
return sorted(frequency) { $0.1 > $1.1 }
}
frequencies(a)
For Swift 2.0, you can adapt the function to be a protocol extension:
extension SequenceType where Generator.Element: Hashable {
func frequencies() -> [(Generator.Element,Int)] {
var frequency: [Generator.Element:Int] = [:]
for x in self {
frequency[x] = (frequency[x] ?? 0) + 1
}
return frequency.sort { $0.1 > $1.1 }
}
}
a.frequencies()
For Swift 3.0:
extension Sequence where Self.Iterator.Element: Hashable {
func frequencies() -> [(Self.Iterator.Element,Int)] {
var frequency: [Self.Iterator.Element:Int] = [:]
for x in self {
frequency[x] = (frequency[x] ?? 0) + 1
}
return frequency.sorted { $0.1 > $1.1 }
}
}
For XCode 7.1 the solution is.
// Array of elements
let a = [7,3,2,1,4,6,8,9,5,3,0,7,2,7]
// Create a key for elements and their frequency
var times: [Int: Int] = [:]
// Iterate over the dictionary
for b in a {
// Every time there is a repeat value add one to that key
times[b] = (times[b] ?? 0) + 1
}
// This is for sorting the values
let descending = times.sort({$0.1 > $1.1})
// For sorting the keys the code would be
// let descending = times.sort({$0.0 > $1.0})
// Do whatever you want with sorted array
print(descending)
Same as Airspeed Velocity, using a reduce instead of for-in:
extension Sequence where Self.Iterator.Element: Hashable {
func frequencies() -> [(Self.Iterator.Element, Int)] {
return reduce([:]) {
var frequencies = $0
frequencies[$1] = (frequencies[$1] ?? 0) + 1
return frequencies
}.sorted { $0.1 > $1.1 }
}
}
But please note that, here, using reduce with a struct is not as efficient as a for-in because of the struct copy cost. So you will generally prefer the for-in way of doing it.
[edit: gosh, the article is by the same guy as the top answer!]
Related
today I did a test for a job and was asked to search through an array of integers, this is the question:
The goal of this exercise is to check the presence of a number in an
array.
Specifications:
The items are integers arranged in ascending order.
The array can contain up to 1 million items
Implement the function existsInArray(_ numbers: [Int], _ k: Int) so
that it returns true if k belongs to numbers, otherwise the function
should return false.
Example:
let numbers = [-9, 14, 37, 102]
existsInArray(numbers, 102) // returns true
existsInArray(numbers, 36) //returns false
Note: Try to save CPU cycles
Alright, so I gave my answer which is the code below and waited for the result
func existsInArray(_ numbers: [Int], _ k: Int) -> Bool {
if numbers.isEmpty {
return false
}
let numbersHalfIndex: Int = (numbers.count/2)
if k == numbers[numbersHalfIndex] {
return true
} else if k != numbers[0] && numbers.count == 1 {
return false
} else if k <= numbers[numbersHalfIndex] {
let leftHalfNumbersArray = numbers[0 ..< numbersHalfIndex]
return existsInArray(Array(leftHalfNumbersArray), k)
} else if k > numbers[numbersHalfIndex] {
let rightHalfNumbersArray = numbers[numbersHalfIndex ..< numbers.count]
return existsInArray(Array(rightHalfNumbersArray), k)
} else {
return false
}
}
So turns out that "The solution doesn't work in a reasonable time with one million items" and now I don't know what I did wrong since binary search is fast as f*ck.
My only guess is that maybe number.count or numbers[0 ...< numbersHalfIndex] or numbers[numbersHalfIndex ...< number.count] makes everything go slower than expected.
Am I tripping or something?
Edit:
If anyone is curious I tested my code and Martin R code to see how much of an impact using ArraySlice have in terms of time.
I used an array of 100.000.000 itens in ascending order starting from 0.
Here is how I captured the time:
print("////////// MINE //////////")
var startTime = CFAbsoluteTimeGetCurrent()
print(existsInArray(numbers, 0))
var timeElapsed = CFAbsoluteTimeGetCurrent() - startTime
print("Time elapsed for mine: \(timeElapsed) s.")
print("////////// Martin R //////////")
counter = 0
startTime = CFAbsoluteTimeGetCurrent()
print(existsInArrayOptimal(numbers, 0))
timeElapsed = CFAbsoluteTimeGetCurrent() - startTime
print("Time elapsed for Martin R: \(timeElapsed) s.")
And here is the result:
////////// MINE //////////
true
Time elapsed for mine:
1.2008800506591797 s.
////////// Martin R //////////
true
Time elapsed for Martin R: 0.00012993812561035156 s.
It's about 1000x faster!
Accessing number.count is not a problem because that is a O(1) operation for arrays. And slicing with numbers[0 ...< numbersHalfIndex] is not a problem either. But Array(leftHalfNumbersArray) creates a new array from the slice, and that copies all the elements.
There are two possible ways to avoid that:
Update array indices (for lower and upper bound of the current search range) instead of creating arrays which are passed down the recursion.
Pass array slices down the recursion. Slices share the elements with the original array (as long as they are not mutated).
A demonstration of the second approach:
func existsInArray(_ numbers: ArraySlice<Int>, _ k: Int) -> Bool {
if numbers.isEmpty {
return false
}
let numbersHalfIndex = numbers.startIndex + numbers.count / 2
if k == numbers[numbersHalfIndex] {
return true
} else if k < numbers[numbersHalfIndex] {
return existsInArray(numbers[..<numbersHalfIndex], k)
} else {
return existsInArray(numbers[(numbersHalfIndex + 1)...], k)
}
}
Note that array slices share their indices with the original array so that the indices do not necessarily start at zero. That's why numbers.startIndex is used for the index calculation.
And a wrapper function which takes a “real” array argument:
func existsInArray(_ numbers: [Int], _ k: Int) -> Bool {
return existsInArray(numbers[...], k)
}
As #Leo suggested, you can implement this as a collection method instead of implementing two separate methods. Collection indices are not necessarily integers, but for a RandomAccessCollection the index calculations are guaranteed to be O(1). You can also generalize it to collections of arbitrary comparable elements instead of integers.
Here is a possible implementation:
extension RandomAccessCollection where Element: Comparable {
/// Returns a Boolean value indicating whether the collection contains the
/// given element. It is assumed that the collection elements are sorted
/// in ascending (non-decreasing) order.
///
/// - Parameter element: The element to find in the collection.
/// - Returns: `true` if the element was found in the collection; otherwise,
/// `false`.
///
/// - Complexity: O(log(*n*)), where *n* is the size of the collection.
func binarySearch(for element: Element) -> Bool {
if isEmpty {
return false
}
let midIndex = index(startIndex, offsetBy: count / 2)
if element == self[midIndex] {
return true
} else if element < self[midIndex] {
return self[..<midIndex].binarySearch(for: element)
} else {
return self[index(after: midIndex)...].binarySearch(for: element)
}
}
}
Usage:
let numbers = [-9, 14, 37, 102]
print(numbers.binarySearch(for: 102)) // true
print(numbers.binarySearch(for: 36)) // false
Alternatively a non-recursive method which updates the indices of the search range:
extension RandomAccessCollection where Element: Comparable {
func binarySearch(for element: Element) -> Bool {
var lo = startIndex
var hi = endIndex
while lo < hi {
let mid = index(lo, offsetBy: distance(from: lo, to: hi) / 2)
if element == self[mid] {
return true
} else if element < self[mid] {
hi = mid
} else {
lo = index(after: mid)
}
}
return false
}
}
I'm trying to solve the following problem, given an array of String, of size n, list all n-tuples from this array, that is:
let A: [String] = ["a","b","c",...]
determine all the tuples
["abc..","bac..",...], of which there are exactly n!.
I've written a solution in Swift, but I'm not quite happy with the result, as it uses closures, making it difficult to iterate over the tuples.
Here's the code, just in case:
public func tuple(seq:[String], value:String, block:(String) -> ()) {
if seq.count > 0 {
for i in 0..<seq.count {
var uu = seq;
let kk:String = uu[i];
uu.remove(at: i)
self.tuple(seq:uu,value: value + kk, block: block)
}
} else {
block(value)
}
}
Anyone with a valid solution without closure?
Using the code from Sequence-based enumeration of permutations in lexicographic order on Code Review (updated for
Swift 4, and with the suggestions from Hamish's answer implemented):
extension Array where Element: Comparable {
/// Replaces the array by the next permutation of its elements in lexicographic
/// order.
///
/// It uses the "Algorithm L (Lexicographic permutation generation)" from
/// Donald E. Knuth, "GENERATING ALL PERMUTATIONS"
/// http://www-cs-faculty.stanford.edu/~uno/fasc2b.ps.gz
///
/// - Returns: `true` if there was a next permutation, and `false` otherwise
/// (i.e. if the array elements were in descending order).
mutating func permute() -> Bool {
// Nothing to do for empty or single-element arrays:
if count <= 1 {
return false
}
// L2: Find last j such that self[j] < self[j+1]. Terminate if no such j
// exists.
var j = count - 2
while j >= 0 && self[j] >= self[j+1] {
j -= 1
}
if j == -1 {
return false
}
// L3: Find last l such that self[j] < self[l], then exchange elements j and l:
var l = count - 1
while self[j] >= self[l] {
l -= 1
}
self.swapAt(j, l)
// L4: Reverse elements j+1 ... count-1:
var lo = j + 1
var hi = count - 1
while lo < hi {
self.swapAt(lo, hi)
lo += 1
hi -= 1
}
return true
}
}
struct PermutationSequence<Element : Comparable> : Sequence, IteratorProtocol {
private var current: [Element]
private var firstIteration = true
init(startingFrom elements: [Element]) {
self.current = elements
}
init<S : Sequence>(_ elements: S) where S.Iterator.Element == Element {
self.current = elements.sorted()
}
mutating func next() -> [Element]? {
var continueIterating = true
// if it's the first iteration, we avoid doing the permute() and reset the flag.
if firstIteration {
firstIteration = false
} else {
continueIterating = current.permute()
}
// if the array changed (and it isn't the first iteration), then return it,
// else we're at the end of the sequence.
return continueIterating ? current : nil
}
}
one can very efficiently iterate over all permutations of an array:
let a = ["a", "b", "c"]
let permSeq = PermutationSequence(startingFrom: a)
for tuple in permSeq {
print(tuple.joined())
}
Each call to the iterator creates the next permutation, and only a
fixed amount of additional storage is needed (one array for the
current permutation, and a boolean variable).
I am not sure why you need the closure to just generate the list. Here is what have used in the past. There is probably a 1 liner using flatmap.
func tuple(_ input:[String])->[String]{
print()
if input.count == 1 {return input}
var output = Array<String>()
for a in 0...input.count-1 {
var temp = input
temp.remove(at: a)
output += tuple(temp).map{input[a]+$0}
}
return output
}
print(tuple(a))
My Array:
let array = [45,12,10,90]
// The number I need in this case is 3
Then I need to grab a value of another array:
let otherArray = [6,6,7,4,0]
I have tried to resolve the problem like this:
let maxPosition = array.max()
let desiredValue = otherArray[maxPosition]
This doesn't seem to work as desired.
Thanks for your help!
The problem there is that max returns the maximum value from your array, not an index. You need to find the index of the maximum value and use it with your other array:
let array = [45,12,10,90]
let otherArray = [6,6,7,4,0]
if let maxValue = array.max(), let index = array.index(of: maxValue) {
let desiredValue = otherArray[index]
print(desiredValue) // 4
}
Another option is to use your collection indices when getting the maximum value:
if let index = array.indices.max(by: { array[$0] < array[$1] }) {
let desiredValue = otherArray[index]
print(desiredValue) // 4
}
Here is another approach:
let array = [45,12,10,90]
let otherArray = [6,6,7,4,0]
var maxValueInArray = array[0]
for i in 1..<array.count{
if array[i] > maxValueInArray{
maxValueInArray = array[i]
}
}
if let maxValueIndex = array.index(of: maxValueInArray){
let desiredValueInOtherArray = otherArray[maxValueIndex]
print("Maximum value in array is \(maxValueInArray) with index \(maxValueIndex). Value in otherArray under index \(maxValueIndex) is \(desiredValueInOtherArray)")
}
You could find the index of the highest value by comparing the elements in the array to its highest value:
let array = [45,12,10,90]
let otherArray = [6,6,7,4,0]
if let index = array.firstIndex(where: { $0 == array.max() }) {
print(otherArray[index]) //4
}
Just be mindful that the two arrays are of different sizes, therefore the index of the highest value in one array could be out-of-bounds in the other array.
I am trying to generate 5 random substrings of six characters each from the alphabet. For example: ABCDEF, RSTUVW, UVWXYZ, etc. These substrings can be duplicates, so generating ABCDEF twice is a not a problem.
When I have these 5 substrings, I want to generate five arrays containing three characters. One of these characters should be the last letter of the substring and the other two letters should be two random unique letters from the entire alphabet.
Example:
Get five random substrings:
[ABCDEF], [RSTUVW], [CDEFGH], [LMNOPQ], [UVWXYZ]
For [ABCDEF] the system could generate [F, H, S] and for [RSTUVW] it could generate [K, Q, W]. As you can see, the three-character arrays always contain the last letter of its substring and two other randomised unique letters.
The above is part of a game for kids to practice the order of the alphabet. In order to generate possible answers I actually need small sets of characters to assign to buttons.
What do you think is the best way to approach this?
Thanks to #vacawama here's a possible solution.
1. Create the alphabet
let alphabet = Array("ABCDEFGHIJKLMNOPQRSTUVWXYZ".characters)
2. Create the 6 "sequences"
let sequences = (0..<5).map { _ -> String in
let startIndex = Int(arc4random_uniform(UInt32(alphabet.count - 5)))
let endIndex = startIndex + 5
return String(alphabet[startIndex...endIndex])
}
["PQRSTU", "DEFGHI", "JKLMNO", "CDEFGH", "KLMNOP"]
3. Get the last char for each sequence
let lastChars = sequences.flatMap { $0.characters.last }
["U", "I", "O", "H", "P"]
4. Build 5 elms
Here's the code snippet
let elms = lastChars.map { char0 -> String in
var tempAlphabet = alphabet
tempAlphabet.removeAtIndex(tempAlphabet.indexOf(char0)!)
let index1 = Int(arc4random_uniform(UInt32(tempAlphabet.count)))
let char1 = tempAlphabet.removeAtIndex(index1)
let index2 = Int(arc4random_uniform(UInt32(tempAlphabet.count)))
let char2 = tempAlphabet[index2]
return String(Array(Set<Character>([char0, char1, char2])))
}
["DUN", "ZIQ", "ROP", "HSW", "PGS"]
Update
Here's another solution to fix the problem hightligted by #dfri in the comments below. The following code snipped could replace the previous bullet 4.
extension Array {
mutating func removeRandom() -> Element {
let index = Int(arc4random_uniform(UInt32(count)))
return removeAtIndex(index)
}
}
var availableChars = Array(Set(alphabet).subtract(lastChars))
let elms = lastChars.map { String([$0, availableChars.removeRandom(), availableChars.removeRandom()]) }
Random substrings:
func randomString(length: Int) -> String {
let charactersString = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
let n = UInt32(charactersString.characters.count)
var out = ""
for _ in 0..<length {
let index = charactersString.startIndex.advancedBy(Int(arc4random_uniform(n)))
out.append(charactersString[index])
}
return out
}
Get 5 Strings:
func createSubstrings() -> [String] {
var array = [String]()
for _ in 0...5 {
array.append(self.randomString(6))
}
return array
}
Last array:
func createFinalStrings() -> [String] {
let substrings = createSubstrings()
var finalStrings = [String]()
let lastChars = substrings.flatMap { $0.characters.last }
for _ in 0...5 {
var string = ""
while string.characters.count < 3 {
let index = Int(arc4random_uniform(5))
let lastChar = lastChars[index]
if string.rangeOfString(String(lastChar)) == nil{
string = string + String(lastChar)
}
}
finalStrings.append(string)
}
return finalStrings
}
I'm not really on point with Swift yet and there is a problem that is starting to be a tad annoying.
I just want to add integer in a double dimensional array but it is always returning the same error code : "fatal error : Array index out of range"
var arrayVolley = [[Int]]()
init(){
self.arrayVolley = [[]]
}
Here is where I try to insert :
func addPoints(score : Int, x : Int, y : Int){
if (score > 11 || score < 0){ //11 will be translated as 10x
println("Error on score value")
}
else {
if (x>6 || y>6){
println("Out of array")
}
else{
arrayVolley[x][y]=score
}
}
}
And this is my main :
var i=0
var j=0
for i in 0...6 {
for j in 0...6{
println("Entrez le score")
var scoreinput=input()
var score = scoreinput.toInt()
distance.addPoints(score!, x: i, y: j)
}
}
Thanks a lot for your help in advance
Try to use append to add the integer to the array it is automatically the next idex. It think if the index was never used it gives an error e.g.
var test = [Int]()
test.append(2) // array is empty so 0 is added as index
test.append(4)
test.append(5) // 2 is added as max index array is not [2,4,5]
test[0] = 3 // works because the index 0 exist cause the where more then 1 element in array -> [3,4,5]
test[4] = 5 // does not work cause index for never added with append
or you intialize the array in the correct size, but it's need a size:
var test = [Int](count: 5, repeatedValue: 0) // [0,0,0,0,0]
test[0] = 3 //[3,0,0,0,0]
test[4] = 5 [3,0,0,0,5]
It hope this helps you if not please feel free to comment.