iOS - selecting second lowest number in integer array - ios

Let's say I have an array of numbers:
let numbers: [Int] = [1,2,3,4,5,6,7,8]
I want to pick out the second lowest number in that array but I don't want to use an index, I know you can pick the lowest and highest integer using min/maxElement dot notation so how would I get the second lowest or the second highest?

As per OP comment that second max, min will be the max and min of array in case if redundancy. I have updated the approach.
var numbers: [Int] = [1,1,2,3,4,4] // or [1,2,3,4,5,6,7,8]
let maxCount = numbers.filter({$0 == numbers.max()}).count
let minCount = numbers.filter({$0 == numbers.min()}).count
let secondHighest = numbers.filter(){
maxCount > 1 ? $0 == numbers.max() : $0 < numbers.max()
}.last
// prints 4 for [1,1,2,3,4,4] and 7 for [1,2,3,4,5,6,7,8]
let secondLowest = numbers.filter(){
minCount > 1 ? $0 == numbers.min() : $0 > numbers.min()
}.first
// prints 1 for [1,1,2,3,4,4] and 2 for [1,2,3,4,5,6,7,8]

(1) Find the lowest value; (2) Remove that value; (3) Find min of the remaining:
let numbers: [Int] = [1,2,3,4,5,6,7,8]
var lowest = numbers.minElement()!
var secondLowest = numbers.filter { $0 > lowest }.minElement()
secondLowest is an optional because in case all values in your array are identical, there is really no "second lowest"

// how to get the second lowest number in an integer Array
func secondLowest(arr: [Int]) -> Int {
let sortedArr = arr.sorted()
return sortedArr[1]
}
print(secondLowest(arr: [1,8,100,4,5,6,7,2,212])) // 2

A direct implementation: (as Sulthan suggested?)
func secondMax(numbers: [Int]) -> Int {
let (_, second) = numbers.reduce((Int.min, Int.min)) {(max2: (first: Int, second: Int), value: Int) in
if value > max2.first {
return (value, max2.first)
} else if value > max2.second {
return (max2.first, value)
} else {
return max2
}
}
return second
}
print(secondMax([1,2,3,4,5,6,7,8])) //->7
print(secondMax([1,1,2,3,4,4])) //->4
print(secondMax([5,5,6,1,2,3,4])) //->5
print(secondMax([5,6,6,1,2,3,4])) //->6

Related

How to create a regular expression to find out repeated character in string?

Eg:
HelloWorld - repeated characters are 5 (l is repeating 3 times and o is repeating 2 times)
Smart2000, repeated characters = 3?(0 is repeating 3 times)
Smart#200#12, repeated characters = 6
I tried with iterating over string
Here is my code with string iteration to find out the repeated character in string.
func countRepeatDigitsIn(keyword : String) -> Int
{
// To keep track of processed symbols
var uniqueCharacters = ""
var repeatCharacterCount = 0
for char in keyword.uppercased() {
let alphabet = String(char)
// If this is already counted, skip it
if (uniqueCharacters.contains(alphabet))
{
repeatCharacterCount += 1
}
// Otherwise, add it to processed symbols
uniqueCharacters += alphabet
}
return repeatCharacterCount
}
HelloWorld - repeated characters are 5 (l is repeating 3 times and o is repeating 2 times
The simplest way to get that result is to take a histogram and then add up all the values that are not 1.
Example:
func histogram(_ s:String) -> [Character:Int] {
var d = [Character:Int]()
for c in s {
d[c, default:0] += 1
}
return d
}
let reps = histogram("helloworld").values.filter{$0 > 1}.reduce(0, +) // 5
let reps2 = histogram("smart2000").values.filter{$0 > 1}.reduce(0, +) // 3
let reps3 = histogram("Smart#200#12").values.filter{$0 > 1}.reduce(0, +) // 6
Here's a fun chain of reductions and filters on the characters of the string.
func countRepeatDigitsIn(keyword : String) -> Int {
let total = Array(keyword.uppercased()).reduce(into: [Character : Int]()) { $0[$1, default: 0] += 1 }.filter { $0.value > 1 }.reduce(0) { $0 + $1.value }
return total
}
for text in ["HelloWorld", "Smart2000", "Smart#200#12"] {
print(text, countRepeatDigitsIn(keyword: text))
}
The first reduce builds a dictionary where the keys are characters and the values is the count for the character. Then the filter removes characters only found once. The second reduce adds the remaining counts.
Group same characters in a dictionary using Dictionary(grouping:by:) and add the counts of values where the count is greater than 1
extension String {
func repeatCount() -> Int {
return Dictionary(grouping: lowercased()) { $0 }.values.filter { $0.count > 1 }.reduce(0) { $0 + $1.count }
}
}
print("HelloWorLd".repeatCount())//5
print("Smart2000".repeatCount())//3
print("Smart#200#12".repeatCount())//6

Exclude element in array when iterating using map

I have code like below
let myNums = getXYZ(nums: [1,2,3,4,5])
func getXYZ(nums: [Int]) -> [Int] {
let newNum = nums.map { (num) -> Int in
if num == 2 {
//do something and continue execution with next element in list like break/fallthrough
return 0
}
return num
}
return newNum
}
print(myNums)`
This prints [1,0,3,4,5]
but i want the output to be [1,3,4,5]. How can I exclude 2? I want to alter the if statement used so as to not include in array when it sees number 2
I have to use .map here but to exclude 2..is there any possibility
Please let me know
I'd simply do a filter as described as your problem, you want to filter the numbers by removing another number.
var myNums = [1, 2, 3, 4, 5]
let excludeNums = [2]
let newNum = myNums.filter({ !excludeNums.contains($0) })
print(newNum) //1, 3, 4, 5
If you need to do a map, you could do a map first then filter.
let newNum = myNums.map({ $0*2 }).filter({ !excludeNums.contains($0) })
print(newNum) //4, 6, 8, 10
This maps to multiplying both by 2 and then filtering by removing the new 2 from the list. If you wanted to remove the initial 2 you would have to filter first then map. Since both return a [Int] you can call the operations in any order, as you deem necessary.
As suggested by #koropok, I had to make below changes
nums.compactMap { (num) -> Int? in
....
if num == 2 {
return nil
}
I suggest you to use filter instead of map:
let myNums = [1,2,3,4,5]
let result1 = myNums.filter{ return $0 != 2 }
print(result1) // This will print [1,3,4,5]
If you must definitely use map, then use compactMap:
let result2 = myNums.compactMap { return $0 == 2 ? nil : $0 }
print(result2) // This will print [1,3,4,5]
Hope this helps
filter is more appropriate than map for your use case.
If you want to exclude only 1 number:
func getXYZ(nums: [Int]) -> [Int] {
return nums.filter { $0 != 2 }
}
If you want to exclude a list of numbers, store those exclusions in a Set since Set.contains runs in O(1) time, whereas Array.contains runs in O(n) time.
func getXYZ(nums: [Int]) -> [Int] {
let excluded: Set<Int> = [2,4]
return nums.filter { !excluded.contains($0) }
}
My solution is based on enumerated() method:
let elements = nums.enumerated().compactMap { (index, value) in
( index == 0 ) ? nil : value
}
enumerated() add element's index as first closure argument

Get Subset of array based on the occurrence of elements

I have an array like:
var arr = [4,1,5,5,3]
I want to fetch subset from the array based on the occurrence of elements in it.
For example:
Elements with frequency 1 is {4,1,3}
Elements with frequency 2 is {5,5}
I followed this StackOverflow question but unable to figure out how to do the above thing.
Is there any way I can do this?
You can use an NSCountedSet to get the count of all elements in arr, then you can build a Dictionary, where the keys will be the number of occurencies for the elements and the values will be Arrays of the elements with key number of occurences. By iterating through Set(arr) rather than simply arr to build the Dictionary, you can make sure that repeating elements are only added once to the Dictionary (so for instance with your original example, 5 wouldn't be added twice as having a frequency of 2).
For the printing, you just need to iterate through the keys of the Dictionary and print the keys along with their corresponding values. I just sorted the keys to make the printing go in ascending order of number of occurences.
let arr = [4,1,5,5,3,2,3,6,2,7,8,2,7,2,8,8,8,7]
let counts = NSCountedSet(array: arr)
var countDict = [Int:[Int]]()
for element in Set(arr) {
countDict[counts.count(for: element), default: []].append(element)
}
countDict
for freq in countDict.keys.sorted() {
print("Elements with frequency \(freq) are {\(countDict[freq]!)}")
}
Output:
Elements with frequency 1 are {[4, 6, 1]}
Elements with frequency 2 are {[5, 3]}
Elements with frequency 3 are {[7]}
Elements with frequency 4 are {[2, 8]}
Swift 3 version:
let arr = [4,1,5,5,3,2,3,6,2,7,8,2,7,2,8,8,8,7]
let counts = NSCountedSet(array: arr)
var countDict = [Int:[Int]]()
for element in Set(arr) {
if countDict[counts.count(for: element)] != nil {
countDict[counts.count(for: element)]!.append(element)
} else {
countDict[counts.count(for: element)] = [element]
}
}
for freq in countDict.keys.sorted() {
print("Elements with frequency \(freq) are {\(countDict[freq]!)}")
}
You just need to get the occurrences of the elements and filter the elements that only occurs once or more than once as shown in this answer:
extension Array where Element: Hashable {
// Swift 4 or later
var occurrences: [Element: Int] {
return reduce(into: [:]) { $0[$1, default: 0] += 1 }
}
// // for Swift 3 or earlier
// var occurrences: [Element: Int] {
// var result: [Element: Int] = [:]
// forEach{ result[$0] = (result[$0] ?? 0) + 1}
// return result
// }
func frequencies(where isIncluded: (Int) -> Bool) -> Array {
return filter{ isIncluded(occurrences[$0] ?? 0) }
}
}
Playground Testing:
let arr = [5, 4, 1, 5, 5, 3, 5, 3]
let frequency1 = arr.frequencies {$0 == 1} // [4, 1]
let frequency2 = arr.frequencies {$0 == 2} // [3, 3]
let frequency3orMore = arr.frequencies {$0 >= 3} // [5, 5, 5, 5]
This is it:
func getSubset(of array: [Int], withFrequency frequency: Int) -> [Int]
{
var counts: [Int: Int] = [:]
for item in array
{
counts[item] = (counts[item] ?? 0) + 1
}
let filtered = counts.filter{ $0.value == frequency}
return Array(filtered.keys)
}
This is pure Swift (not using good old Next Step classes) and is using ideas from the SO link you supplied.
The counts dictionary contains the frequencies (value) of each of the int-values (key) in your array: [int-value : frequency].

How can I perform an Array Slice in Swift?

var mentions = ["#alex", "#jason", "#jessica", "#john"]
I want to limit my array to 3 items, so I want to splice it:
var slice = [String]()
if mentions.count > 3 {
slice = mentions[0...3] //alex, jason, jessica
} else {
slice = mentions
}
However, I'm getting:
Ambiguous subscript with base type '[String]' and index type 'Range'
Apple Swift version 2.2 (swiftlang-703.0.18.8 clang-703.0.31)
Target: x86_64-apple-macosx10.9
The problem is that mentions[0...3] returns an ArraySlice<String>, not an Array<String>. Therefore you could first use the Array(_:) initialiser in order to convert the slice into an array:
let first3Elements : [String] // An Array of up to the first 3 elements.
if mentions.count >= 3 {
first3Elements = Array(mentions[0 ..< 3])
} else {
first3Elements = mentions
}
Or if you want to use an ArraySlice (they are useful for intermediate computations, as they present a 'view' onto the original array, but are not designed for long term storage), you could subscript mentions with the full range of indices in your else:
let slice : ArraySlice<String> // An ArraySlice of up to the first 3 elements
if mentions.count >= 3 {
slice = mentions[0 ..< 3]
} else {
slice = mentions[mentions.indices] // in Swift 4: slice = mentions[...]
}
Although the simplest solution by far would be just to use the prefix(_:) method, which will return an ArraySlice of the first n elements, or a slice of the entire array if n exceeds the array count:
let slice = mentions.prefix(3) // ArraySlice of up to the first 3 elements
We can do like this,
let arr = [10,20,30,40,50]
let slicedArray = arr[1...3]
if you want to convert sliced array to normal array,
let arrayOfInts = Array(slicedArray)
You can try .prefix().
Returns a subsequence, up to the specified maximum length, containing the initial elements of the collection.
If the maximum length exceeds the number of elements in the collection, the result contains all the elements in the collection.
let numbers = [1, 2, 3, 4, 5]
print(numbers.prefix(2)) // Prints "[1, 2]"
print(numbers.prefix(10)) // Prints "[1, 2, 3, 4, 5]"
General solution:
extension Array {
func slice(size: Int) -> [[Element]] {
(0...(count / size)).map{Array(self[($0 * size)..<(Swift.min($0 * size + size, count))])}
}
}
Can also look at dropLast() function:
var mentions:[String] = ["#alex", "#jason", "#jessica", "#john"]
var slice:[String] = mentions
if mentions.count > 3 {
slice = Array(mentions.dropLast(mentions.count - 3))
}
//print(slice) => ["#alex", "#jason", "#jessica"]
I came up with this:
public extension Array {
func slice(count: Int) -> [some Collection] {
let n = self.count / count // quotient
let i = n * count // index
let r = self.count % count // remainder
let slices = (0..<n).map { $0 * count }.map { self[$0 ..< $0 + count] }
return (r > 0) ? slices + [self[i..<i + r]] : slices
}
}
You can also slice like this:
//Generic Method
func slice<T>(arrayList:[T], limit:Int) -> [T]{
return Array(arrayList[..<limit])
}
//How to Use
let firstThreeElements = slice(arrayList: ["#alex", "#jason", "#jessica", "#john"], limit: 3)
Array slice func extension:
extension Array {
func slice(with sliceSize: Int) -> [[Element]] {
guard self.count > 0 else { return [] }
var range = self.count / sliceSize
if self.count.isMultiple(of: sliceSize) {
range -= 1
}
return (0...range).map { Array(self[($0 * sliceSize)..<(Swift.min(($0 + 1) * sliceSize, self.count))]) }
}
}

Generate a Swift array of nonrepeating random numbers

I'd like to generate multiple different random numbers in Swift. Here is the procedure.
Set up an empty array
Generate a random number
Check if the array is empty
a. If the array is empty, insert the random number
b. If the array is not empty, compare the random number to the numbers in array
i. If the numbers are the same, repeat 2
ii. if the numbers are not the same, insert the random number and repeat 2
import UIKit
//the random number generator
func randomInt(min: Int, max:Int) -> Int {
return min + Int(arc4random_uniform(UInt32(max - min + 1)))
}
var temp = [Int]()
for var i = 0; i<4; i++ {
var randomNumber = randomInt(1, 5)
if temp.isEmpty{
temp.append(randomNumber)
} else {
//I don't know how to continue...
}
}
If you use your method the problem is, that you will create a new random-number each time. So you possibly could have the same random-number 4 times and so your array will only have one element.
So, if you just want to have an array of numbers from within a specific range of numbers (for example 0-100), in a random order, you can first fill an array with numbers in 'normal' order. For example with for loop etc:
var min = 1
var max = 5
for var i = min; i<= max; i++ {
temp.append(i)
}
After that, you can use a shuffle method to shuffle all elements of the array with the shuffle method from this answer:
func shuffle<C: MutableCollectionType where C.Index == Int>(var list: C) -> C {
let count = countElements(list)
for i in 0..<(count - 1) {
let j = Int(arc4random_uniform(UInt32(count - i))) + i
swap(&list[i], &list[j])
}
return list
}
Ater that you can do something like that:
shuffle(temp) // e.g., [3, 1, 2, 4, 5]
The construct you’re looking for with your approach might be something like:
var temp: [Int] = []
while temp.count < 4 {
var randomNumber: Int
do {
randomNumber = randomInt(1, 5)
} while contains(temp, randomNumber)
temp.append(randomNumber)
}
This will be fine for tiny ranges like yours, but for larger ranges it will be very slow, because for the last few numbers you are waiting for the random number to hit precisely the remaining handful of possibilities. I just tried generating from a range of 200 in a playground and it took 9 seconds.
If you want a random selection of numbers with guaranteed coverage over a range, you could generate it like by taking that range and shuffling it, like this:
func shuffle<S: SequenceType>(source: S) -> [S.Generator.Element] {
var rangen = GeneratorOf { arc4random() }
let a = Array(Zip2(rangen, source))
return a.sorted { $0.0 < $1.0 }.map { $0.1 }
}
let min = 1, max = 5
shuffle(min...max)
If you want a selection of n non-repeating random numbers from a range 0..<m, there’s a particularly pretty algorithm to do this that generates an ascending sequence of random numbers from that range:
func randomGeneratorOf(#n: Int, #from: Int) -> GeneratorOf<Int> {
var select = UInt32(n)
var remaining = UInt32(from)
var i = 0
return GeneratorOf {
while i < from {
if arc4random_uniform(remaining) < select {
--select
--remaining
return i++
}
else {
--remaining
++i
}
}
return nil
}
}
Which you could use like so:
let engines = [
"Duck","Emily","Gordon","Henry", "Mavis",
"Belle","James","Edward","Thomas","Toby"
]
let picks = Array(randomGeneratorOf(n: 3, from: engines.count))
for engine in PermutationGenerator(elements: engines, indices: picks) {
println(engine)
}
Below is my suggestion.
I like this way since it is short and simple :)
let totalCount: Int = 150 //Any number you asssign
var randomNumArray: [Int] = []
var i = 0
while randomNumArray.count < totalCount {
i++
let rand = Int(arc4random_uniform(UInt32(totalCount)))
for(var ii = 0; ii < totalCount; ii++){
if randomNumArray.contains(rand){
print("do nothing")
} else {
randomNumArray.append(rand)
}
}
}

Resources