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]
Related
I am learning Quick Sort in swift and need to compose a complicated array.
Here is the code:
var arrayOne = 1...500
var arrayTwo = 501...1000
var array_one = arrayOne.reversed()
var array_two = arrayTwo.reversed()
var array = arrayOne + arrayTwo
I want to combine arrayOne + arrayTwo to array.
I can not use the + operator, Xcode tips me
Binary operator '+' cannot be applied to two
'CountableClosedRange' operands
I know how to get it by using for loops.
Elegant way is really needed. Such as Higher order function.
1...500 is a range and (1...500).reversed() is a collection. Both are sequences so that you can append them to an array:
let rangeOne = 1...500
let rangeTwo = 501...1000
let array = [] + rangeOne.reversed() + rangeTwo.reversed()
// [500, 499, ..., 2, 1, 1000, 999, ..., 502, 501]
Alternative solutions are:
let array = Array(rangeOne.reversed()) + rangeTwo.reversed()
let array = Array([rangeOne.reversed(), rangeTwo.reversed()].joined())
let array = Array(rangeOne.reversed()) + Array(rangeTwo.reversed())
let array = [rangeOne.reversed(), rangeTwo.reversed()].flatMap { $0 }
I need to convert a Double to big-endian in order to write it to a file, using an oil-industry binary file standard, that was originally defined for IBM half-inch 9 track tapes in the 1970s!
I need really efficient Swift 4 code, because this conversion is inside two nested-loops and will be executed upwards of 100,000 times.
You can create an UInt64 containing the big-endian representation
of the Double with
let value = 1.0
var n = value.bitPattern.bigEndian
In order to write that to a file you might need to convert it
to Data:
let data = Data(buffer: UnsafeBufferPointer(start: &n, count: 1))
print(data as NSData) // <3ff00000 00000000>
If many contiguous floating point values are written to the file
then it would be more effective to create an [UInt64] array
with the big-endian representations and convert that to Data,
for example
let values = [1.0, 2.0, 3.0, 4.0]
let array = values.map { $0.bitPattern.bigEndian }
let data = array.withUnsafeBufferPointer { Data(buffer: $0) }
(All the above compiles with Swift 3 and 4.)
I successfully implemented Martin's array suggestion. I decided I should use some "interesting" test values and one thing led to another! Here's my test playground. I hope it is of interest:
//: Playground - noun: a place where people can play
import UIKit
func convert(doubleArray: [Double]) {
let littleEndianArray = doubleArray.map { $0.bitPattern}
var data = littleEndianArray.withUnsafeBufferPointer { Data(buffer: $0) }
print("Little-endian : ", data as NSData)
// Convert and display the big-endian bytes
let bigEndianArray = doubleArray.map { $0.bitPattern.bigEndian }
data = bigEndianArray.withUnsafeBufferPointer { Data(buffer: $0) }
print("Big-endian : ", data as NSData)
}
// Values below are from:
// https://en.wikipedia.org/wiki/Double-precision_floating-point_format
let nan = Double.nan
let plusInfinity = +1.0 / 0.0
let maxDouble = +1.7976931348623157E308
let smallestNumberGreaterThanOne = +1.0000000000000002
let plusOne = +1.0
let maxSubnormalPositiveDouble = +2.2250738585072009E-308
let minSubnormalPositiveDouble = +4.9E-324
let plusZero = +0.0
let minusZero = -0.0
let maxSubnormalNegativeDouble = -4.9E-324
let minSubnormalNegativeDouble = -2.2250738585072009E-308
let minusOne = -1.0
let largestNumberLessThanOne = -1.0000000000000002
let minDouble = -1.7976931348623157E308
let minusInfinity = -1.0 / 0.0
let smallestNumber = "+1.0000000000000002"
let largestNumber = "-1.0000000000000002"
print("\n\nPrint little-endian and big-endian Doubles")
print("\n\nDisplay: NaN and +0.0 to +1.0")
print(" Min. Subnormal Max. Subnormal")
print(" Not a Number Plus Zero Positive Double Positive Double Plus One")
print(String(format: "Decimal : NaN %+8.6e %+8.6e %+8.6e %+8.6e", plusZero, minSubnormalPositiveDouble, maxSubnormalPositiveDouble, plusOne))
var doubleArray = [nan, plusZero, minSubnormalPositiveDouble, maxSubnormalPositiveDouble, plusOne]
convert(doubleArray: doubleArray)
print("\n\nDisplay: +1.0 to +Infinity")
print(" Smallest Number ")
print(" Plus One Greater Than 1.0 Max. Double +Infinity")
print(String(format: "Decimal : %+8.6e \(smallestNumber) %+8.6e%+8.6e", plusOne, maxDouble, plusInfinity))
doubleArray = [plusOne, smallestNumberGreaterThanOne, maxDouble, plusInfinity]
convert(doubleArray: doubleArray)
print("\n\nDisplay: NaN and -0.0 to -1.0")
print(" Min. Subnormal Max. Subnormal")
print(" Not a Number Minus Zero Negative Double Negative Double Minus One")
print(String(format: "Decimal : NaN %+8.6e %+8.6e %+8.6e %+8.6e", minusZero, maxSubnormalNegativeDouble, minSubnormalNegativeDouble, minusOne))
doubleArray = [nan, minusZero, maxSubnormalNegativeDouble, minSubnormalNegativeDouble, minusOne]
convert(doubleArray: doubleArray)
print("\n\nDisplay: -1.0 to -Infinity")
print(" Smallest Number ")
print(" Minus One Less Than -1.0 Min. Double -Infinity")
print(String(format: "Decimal : %+8.6e \(largestNumber) %+8.6e%+8.6e", minusOne, minDouble, minusInfinity))
doubleArray = [minusOne, largestNumberLessThanOne, minDouble, minusInfinity]
convert(doubleArray: doubleArray)
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}
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.
Imagine we have an arbitrary Range<T> and we want to create a new range with startIndex and endIndex advanced by 50 units.
My first thought was to do this:
let startIndex = advance(range.startIndex, 50)
let endIndex = advance(range.endIndex, 50)
var newRange = startIndex..<endIndex
But this gives "fatal error: can not increment endIndex". (Well, it does with Range<String.Index>. I haven't tried it with other generic parameters.) I've tried quite a few permutations of this, including assigning range.startIndex and range.endIndex to new variables, etc. Nothing works.
Let me stress that I'm looking for a solution that works with any T. GoZoner gave an answer below that I haven't tried with Int, but I wouldn't be surprised if it worked. However, no permutation of it I tried will work when T is String.Index
So, how can I do this?
There’s a second version of advance that takes a maximum index not to go beyond:
let s = "Hello, I must be going."
let range = s.startIndex..<s.endIndex
let startIndex = advance(range.startIndex, 50, s.endIndex)
let endIndex = advance(range.endIndex, 50, s.endIndex)
var newRange = startIndex..<endIndex
if newRange.isEmpty {
println("new range out of bounds")
}
Try:
1> var r1 = 1..<50
r1: Range<Int> = 1..<50
2> var r2 = (r1.startIndex+50)..<(r1.endIndex+50)
r2: Range<Int> = 51..<100