Swift 3: Filter a range - swift

In Swift 2 it was possible to filter a range like this:
let range: Range<Int> = 1..<100
let mult4 = range
.filter{n in n % 4 == 0}
In Swift3 the range seems to have lost its filter method. Any suggestions?

You have to use a countable range:
let range: CountableRange<Int> = 1..<100
// Or simply: let range = 1..<100
let mult4 = range.filter { n in n % 4 == 0 }
A (Closed)Range describes an "interval" and can not be enumerated,
whereas a Countable(Closed)Range is a collection of consecutive values.

You can use stride:
let mult4 = Array(stride(from: 0, to: 100, by: 4))

let range: Range<Int> = 1..<100
let mult4 = [Int](range.lowerBound..<range.upperBound).filter{n in n % 4 == 0}

Related

Get new array of values from within sorted array

I have an array of SCNNode that are sorted by their y positions(Float):
nodesSortedByY = scene.rootNode.childNodes.sorted { $0.position.y > $1.position.y }
What I would like to do is get a new array from nodesSortedByY where the y values are within a certain range in a similar way to how subscript works but by passing actual values not indexes.
For example:
let nodesSortedByY = [5.0, 4.0, 4.0, 3.0, 2.0, 2.0, 1.0]
let subRange = nodesSortedByY(4.0...2.0)
print(subRange) // [4.0, 4.0, 3.0, 2.0, 2.0]
I tried using indexes originally combined with this binary search but it doesnt work if the values dont exist within the array:
let yPositions = nodesSortedByY.map({ $0.position.y })
let firstIndex = yPositions.binarySearch(forFirstIndexOf: firstValue) ?? 0
let lastIndex = yPositions.binarySearch(forLastIndexOf: lastValue) ?? 0
nodesSortedByY[lastIndex...firstIndex]
What you want is to filter().
let sub = nodesSortedByY.filter { (2.0...4.0).contains($0.position.y) }
We keep only the elements in nodesSortedByY where its y position is inside the range [2.0; 4.0].
Since you sorted your array (descending order), you can applied that logic too (modification of your attempt)
let lowerBound = nodesSortedByY.firstIndex(where: { $0 <= 4.0 }) ?? nodesSortedByY.startIndex
let upperBound = nodesSortedByY.lastIndex(where: { $0 >= 2.0 }) ?? nodesSortedByY.endIndex
let sub = nodesSortedByY[lowerBound...upperBound]

Swift - Using stride with an Int Array

I want to add the numbers together and print every 4 elements, however i cannot wrap my head around using the stride function, if i am using the wrong approach please explain a better method
var numbers = [1,2,3,4,5,6,7,8,9,10,11,12,13]
func addNumbersByStride(){
var output = Stride...
//first output = 1+2+3+4 = 10
//second output = 5+6+7+8 = 26 and so on
print(output)
}
It seems you would like to use stride ...
let arr = [1,2,3,4,5,6,7,8,9,10,11,12,13]
let by = 4
let i = stride(from: arr.startIndex, to: arr.endIndex, by: by)
var j = i.makeIterator()
while let n = j.next() {
let e = min(n.advanced(by: by), arr.endIndex)
let sum = arr[n..<e].reduce(0, +)
print("summ of arr[\(n)..<\(e)]", sum)
}
prints
summ of arr[0..<4] 10
summ of arr[4..<8] 26
summ of arr[8..<12] 42
summ of arr[12..<13] 13
You can first split the array into chunks, and then add the chunks up:
extension Array {
// split array into chunks of n
func chunked(into size: Int) -> [[Element]] {
return stride(from: 0, to: count, by: size).map {
Array(self[$0 ..< Swift.min($0 + size, count)])
}
}
}
// add each chunk up:
let results = numbers.chunked(into: 4).map { $0.reduce(0, +) }
If you would like to discard the last sum if the length of the original array is not divisible by 4, you can add an if statement like this:
let results: [Int]
if numbers.count % 4 != 0 {
results = Array(numbers.chunked(into: 4).map { $0.reduce(0, +) }.dropLast())
} else {
results = numbers.chunked(into: 4).map { $0.reduce(0, +) }
}
This is quite a basic solution and maybe not so elegant. First calculate and print sum of every group of 4 elements
var sum = 0
var count = 0
for n in stride(from: 4, to: numbers.count, by: 4) {
sum = 0
for i in n-4..<n {
sum += numbers[i]
}
count = n
print(sum)
}
Then calculate the sum of the remaining elements
sum = 0
for n in count..<numbers.count {
sum += numbers[n]
}
print(sum)

Calling different extent of randomness of arc4random in Swift?

This might be rather stupid question. I would like to know if different nuances/extent of randomness would be possible using arc4random_uniform in Swift. Here's an example:
let number = arc4random_uniform(10) + 1
print(number)
In this case, a number will be printed randomly from 1 to 10. But is there a way that I can repeat the random result, 2 to 3 times? The result would be something like this:
1, 1, 6, 6, 6, 3, 3, 8, 8, 9, 9, 9 ...
// 1) Randomly selected and 2) repeated 2 to 3 times randomly.
Perhaps I might use two arc4random_uniform functions together, but cannot express them properly. Would be much appreciated if you could give me some suggestions. <3
In order to do this, you will need to generate two values: your random value and a repeatCount. Also, you'll need to remember both of those values so that you can repeat the value. You can do this with a custom class:
class RandomWithRepeats {
var range: ClosedRange<Int>
var repeatRange: ClosedRange<Int>
var repeatCount = 0
var value = 0
init(range: ClosedRange<Int>, repeatRange: ClosedRange<Int>) {
self.range = range
self.repeatRange = repeatRange
}
// generate a random number in a range
// Just use Int.random(in:) with Swift 4.2 and later
func random(in range: ClosedRange<Int>) -> Int {
return Int(arc4random_uniform(UInt32(range.upperBound - range.lowerBound + 1))) + range.lowerBound
}
func nextValue() -> Int {
// if repeatCount is 0, its time to generate a new value and
// a new repeatCount
if repeatCount == 0 {
// For Swift 4.2, just use Int.random(in:) instead
value = self.random(in: range)
repeatCount = self.random(in: repeatRange)
}
repeatCount -= 1
return value
}
}
Example:
let rand = RandomWithRepeats(range: 1...10, repeatRange: 2...3)
// generate 20 random repeated numbers
for _ in 1...20
{
print(rand.nextValue(), terminator: " ")
}
6 6 6 8 8 8 10 10 10 2 2 9 9 5 5 8 8 8 5 5
With regards to the nuances of random number generators: have a look at GKRandomSource.
What you're doing here is not really making something less random, or modifying the parameters in the random number generator. You're simply applying an operation (with one random parameter) to a collection of random integers.
extension Collection {
func duplicateItemsRandomly(range: CountableClosedRange<Int>) -> [Element] {
return self.reduce(into: [Element](), { (acc, element) in
let distance = UInt32(range.upperBound - range.lowerBound + 1)
let count = Int(arc4random_uniform(distance) + UInt32(range.lowerBound))
let result = Array.init(repeating: element, count: count)
acc.append(contentsOf: result)
})
}
}
let sequence = [1, 6, 3, 8, 9]
sequence.duplicateItemsRandomly(range: 2...3)
// [1, 1, 6, 6, 6, 3, 3, 3, 8, 8, 8, 9, 9, 9]
P.S: If you're writing this code in Swift 4.2, please use Int.random(in:).
I'd suggest a custom Sequence:
class RepeatingRandomSequence : Sequence {
let rangeLow, rangeSpan : UInt32
let repeatLow, repeatSpan : UInt32
init(range:Range<UInt32>, count:Range<UInt32>) {
rangeLow = range.lowerBound
rangeSpan = range.upperBound - range.lowerBound + 1
repeatLow = count.lowerBound
repeatSpan = count.upperBound - count.lowerBound + 1
}
func makeIterator() -> AnyIterator<UInt32> {
var count : UInt32 = 0
var value : UInt32 = 0
return AnyIterator {
if(count <= 0) {
count = arc4random_uniform(self.repeatSpan) + self.repeatLow
value = arc4random_uniform(self.rangeSpan) + self.rangeLow
}
defer { count = count - 1 }
return value
}
}
}
let sequence = RepeatingRandomSequence(range: 0..<10, count: 2..<3)
let randoms = sequence.makeIterator()
Note that the iterator, randoms now generates an endless sequence of random numbers using randoms.next() Since the sequence is endless, many things aren't particularly useful, like sort, map, etc. You could however use it like:
for value in random {
print(value)
if(value == 9) { // or any other termination condition
break
}
}
Or more conventionally, as:
(0..<10).forEach { _ in
print(String(describing: random.next()))
}

Swift Range Type endIndex

If you create a var Range = 0...0, I would expect the endIndex to be zero. But in reality is 1.
var myRange: Range<Int> = 0...0
print("start Index \(myRange.startIndex) End Index \(myRange.endIndex)")
output: "start Index 0 End Index 1"
How can I question a Range instance if an Index of type Int is contained ?
The endIndex is not actually included in the Range. The Range is startIndex ..< endIndex. So, for your example, 0...0 is stored as 0..<1 which means the same thing.
For Swift 1.2 you can use the global function contains to check if an Int is contained by a Range:
var myRange: Range<Int> = 0...0
let i: Int = 1
if contains(myRange, i) {
println("yes")
} else {
println("no") // prints "no"
}
For Swift 2.0:
var myRange: Range<Int> = 0...0
let i: Int = 1
if myRange.contains(i) {
print("yes")
} else {
print("no") // prints "no"
}
Maybe you could refer to Half-Open Range Operator
var myRange: Range<Int> = 0..<0
outputs:"start Index 0 End Index 0"
The half-open range operator (a..<b) defines a range that runs from a to b, but does not include b. And the closed range operator (a...b) will finally turn to (a..<b+1)
Because Range is also a collection, you can use its minElement() and maxElement() methods, which will return the correct index, respecting the range being closed (...) or half-open (..<).
So the below code will output zeros as expected:
let range: Range<Int> = 0...0
let min = range.minElement()!
let max = range.maxElement()!
print("min=\(min), max=\(max)")
// Output: "min=0, max=0"
Note: both methods have O(elements.count) complexity which might not be suitable for some cases.

Swift - Turn Int to binary representations

I receive an Int from my server which I’d like to explode in to an array of bit masks. So for example, if my server gives me the number 3, we get two values, a binary 1 and a binary 2.
How do I do this in Swift?
You could use:
let number = 3
//radix: 2 is binary, if you wanted hex you could do radix: 16
let str = String(number, radix: 2)
println(str)
prints "11"
let number = 79
//radix: 2 is binary, if you wanted hex you could do radix: 16
let str = String(number, radix: 16)
println(str)
prints "4f"
I am not aware of any nice built-in way, but you could use this:
var i = 3
let a = 0..<8
var b = a.map { Int(i & (1 << $0)) }
// b = [1, 2, 0, 0, 0, 0, 0, 0]
Here is a straightforward implementation:
func intToMasks(var n: Int) -> [Int] {
var masks = [Int]()
var mask = 1
while n > 0 {
if n & mask > 0 {
masks.append(mask)
n -= mask
}
mask <<= 1
}
return masks
}
println(intToMasks(3)) // prints "[1,2]"
println(intToMasks(1000)) // prints "[8,32,64,128,256,512]"
public extension UnsignedInteger {
/// The digits that make up this number.
/// - Parameter radix: The base the result will use.
func digits(radix: Self = 10) -> [Self] {
sequence(state: self) { quotient in
guard quotient > 0
else { return nil }
let division = quotient.quotientAndRemainder(dividingBy: radix)
quotient = division.quotient
return division.remainder
}
.reversed()
}
}
let digits = (6 as UInt).digits(radix: 0b10) // [1, 1, 0]
digits.reversed().enumerated().map { $1 << $0 } // [0, 2, 4]
Reverse the result too, if you need it.