swift range greater than lower bound

8.1k views Asked by At

I need to implement experience filter like this

  • 0 to 2 years
  • 2+ to 4 years

How to express it in swift range?

Problem is I can't express more than 2 to 4 years. While I can do less than upper bounds. e.g. like this

let underTen = 0.0..<10.0

I need something like this (greater than lower bound)

let uptoTwo = 0.0...2.0
let twoPlus = 2.0>..4.0  // compiler error

Currently I am doing

let twoPlus = 2.1...4.0

But this is not perfect.

4

There are 4 answers

2
dfrib On BEST ANSWER

nextUp from the FloatingPoint protocol

You can make use of the nextUp property of Double, as blueprinted in the FloatingPoint protocol to which Double conforms

nextUp

The least representable value that compares greater than this value.

For any finite value x, x.nextUp is greater than x. ...

I.e.:

let uptoTwo = 0.0...2.0
let twoPlus = 2.0.nextUp...4.0

The property ulp, also blueprinted in the FloatingPoint protocol, has been mentioned in the comments to your question. For most numbers, this is the difference between self and the next greater representable number:

ulp

The unit in the last place of self.

This is the unit of the least significant digit in the significand of self. For most numbers x, this is the difference between x and the next greater (in magnitude) representable number. ...

nextUp does, in essence, return the value of self with the addition of ulp. So for your example above, the following is equivalent (whereas, imo, nextup should be preferred in this use case).

let uptoTwo = 0.0...2.0 
let twoPlus = (2.0+2.0.ulp)...4.0

You might also want to consider replacing the lower bound literal in twoPlus with the upperBound property of the preceding uptoTwo range:

let uptoTwo = 0.0...2.0                       // [0, 2] closed-closed
let twoPlus = uptoTwo.upperBound.nextUp...4.0 // (2, 4] open-closed

if uptoTwo.overlaps(twoPlus) {
    print("the two ranges overlap ...")
}
else {
    print("ranges are non-overlapping, ok!")
}
// ranges are non-overlapping, ok!
1
OOPer On

If your actual purpose is to use ranges for filtering, how about making them as closures?

let underTen = {0.0 <= $0 && $0 < 10.0}
let upToTwo = {0.0 <= $0 && $0 <= 2.0}
let twoPlus = {2.0 <  $0 && $0 <= 4.0}

You can use such filtering closures like this:

class Client: CustomStringConvertible {
    var experience: Double
    init(experience: Double) {self.experience = experience}
    var description: String {return "Client(\(experience))"}
}
let clients = [Client(experience: 1.0),Client(experience: 2.0),Client(experience: 3.0)]
let filteredUnderTen = clients.filter {underTen($0.experience)}
print(filteredUnderTen) //->[Client(1.0), Client(2.0), Client(3.0)]
let filteredUpToTwo = clients.filter {upToTwo($0.experience)}
print(filteredUpToTwo) //->[Client(1.0), Client(2.0)]
let filteredTwoPlus = clients.filter {twoPlus($0.experience)}
print(filteredTwoPlus) //->[Client(3.0)]
1
sketchyTech On

Rather than create a new type of range you can instead create a method that will identify values above the lower bound:

extension ClosedRange {
    func containsAboveLowerBound(value:Bound) -> Bool {
        if value > self.lowerBound {
            return self.contains(value)
        }
        else {
            return false
        }
    }
}

implementing it like so:

let r = 2.0...3.0
r.containsAboveLowerBound(value: 2.0) // false
r.containsAboveLowerBound(value: 2.01) // true
0
Mohammad Abdurraafay On

I think this does it,

extension ClosedRange where Bound == Int {
    func containsExclusiveOfBounds(_ bound: Bound) -> Bool {
        return !(bound == lowerBound || bound == upperBound)
    }
}