What are intervals in swift ranges?
As of Swift 3 (with Xcode 8), the Interval
types are no more. Now the family of Range<T>
types include the functionality of both the former range and interval types, and additionally conform to the new model for collection types and indices.
In Swift 2.x and older... Ranges are for iterating, and Intervals are for pattern matching.
func testNum(num: Int) {
let interval: HalfOpenInterval = 0..<10
let range = 10..<20
switch num {
case interval: // this works
break
case range: // error "does not conform to protocol IntervalType"
break
default:
break
}
}
A Range
type is optimized for generating values that increment through the range, and works with types that can be counted and incremented.
An Interval
type is optimized for testing whether a given value lies within the interval. It works with types that don't necessarily need a notion of incrementing, and provides operations like clamping one range to another (e.g. (0..<10).clamp(5..<15)
yields 5..<10
) that are useful for complex pattern matching.
Because the ..<
and ...
operators have two forms each--one that returns a Range
and one that returns an Interval
--type inference automatically uses the right one based on context. So, if you write 0..<10
in a case
label of a switch
statement, Swift automatically constructs a HalfOpenInterval
because a switch
statement requires an Interval
type.
The ~=
operator is a way to do one test on an interval without a switch
statement. Writing interval ~= value
is equivalent to interval.contains(value)
.
It's worth noting that you can find out many of these things by looking at the standard library interface and its comments: write a type name like HalfOpenInterval
in a playground, then command-click to jump to its definition.
Switch case with range
This should work.
private func calculateUserScore() -> Int {
let diff = abs(randomNumber - Int(bullsEyeSlider.value))
switch diff {
case 0:
return PointsAward.bullseye.rawValue
case 1..<10:
return PointsAward.almostBullseye.rawValue
case 10..<30:
return PointsAward.close.rawValue
default:
return 0
}
}
It's there in the The Swift Programming Language book under Control Flow -> Interval Matching.
How does one iterate over an Array of Ranges?
The problem is not that you have an array of ranges, but that ClosedRange
in Swift 3 represents
An interval over a comparable type, from a lower bound up to, and including, an upper bound.
For example, a closed range can be used with Double
let r: ClosedRange<Double> = 1.1...2.2
where enumerating all possible values does not make much sense.
What you need is CountableClosedRange
which is
A closed range that forms a collection of consecutive values.
and in particular is a collection and can be iterated over:
let a = 0
let b = 10
var arr = [CountableClosedRange<Int>]()
let myRange: CountableClosedRange = a...b
arr.append(myRange)
for each in arr {
for every in each {
print(every)
}
}
You can just write
let myRange = a...b
since by default, the ...
operator produces a CountableClosedRange
if its operands are Strideable
.
Similarly there is Range
and CountableRange
for half-open ranges.
For more information, see Range Types in SE-0065 A New Model for Collections and Indices.
How do string intervals work?
As I understand it, ClosedInterval(lo, hi)
represents all strings s
such that
lo <= s <= hi
and HalfOpenInterval(lo, hi)
represents all strings s
such that
lo <= s < hi
where <=
and <
are determined by the lexicographic ordering of the Strings,
i.e. be comparing the characters one by one, until a difference is found.
For example "QED" < "T" < "WTF"
because Q < T < W
, but "F" < "QED"
becauseF < Q
. Therefore
HalfOpenInterval("QED" , "WTF").contains("T") == true
HalfOpenInterval("QED" , "WTF").contains("F") == false
And "QED" < "QSF"
because Q == Q
and E < S
, but "QAF" < "QED"
because Q == Q
and A < E
. Therefore
HalfOpenInterval("QED" , "WTF").contains("QSF") == true
HalfOpenInterval("QED" , "WTF").contains("QAF") == false
That should explain all your test results.
Finally,
let clampedString0 = aThroughHHalfOpen.clamp ( tThroughXHalfOpen )
is an empty interval because "A"..<"H"
and "T"..<"X"
have no points in common.
An empty interval can be represented by HalfOpenInterval(dummy, dummy)
for any
value of dummy
.
How to determine when multiple(n) datetime ranges overlap each other
You need to do the following steps:
- Convert your date string to Date objects.
- Create DateIntervals with your start and end date objects.
- Loop through the intervals and check for intersection.
Here is a quick code I can come up with in swift:
func answer() {
let dateFormat = "yyyy-MM-dd HH:mm Z"
// Date ranges
let times = [["start": "2016-01-01 12:00 +0000", "end": "2016-05-01 03:00 +0000"],
["start": "2016-01-01 03:00 +0000", "end": "2016-05-01 03:00 +0000"],
["start": "2016-01-01 03:00 +0000", "end": "2016-04-30 13:31 +0000"]]
var intervals = [DateInterval]()
// Loop through date ranges to convert them to date intervals
for item in times {
if let start = convertStringToDate(string: item["start"]!, withFormat: dateFormat),
let end = convertStringToDate(string: item["end"]!, withFormat: dateFormat) {
intervals.append(DateInterval(start: start, end: end))
}
}
// Check for intersection
let intersection = intersect(intervals: intervals)
print(intersection)
}
// Converts the string to date with given format
func convertStringToDate(string: String, withFormat format: String) -> Date? {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = format
return dateFormatter.date(from: string)
}
// Cehck for intersection among the intervals in the given array and return
// the interval if found.
func intersect(intervals: [DateInterval]) -> DateInterval? {
// Algorithm:
// We will compare first two intervals.
// If an intersection is found, we will save the resultant interval
// and compare it with the next interval in the array.
// If no intersection is found at any iteration
// it means the intervals in the array are disjoint. Break the loop and return nil
// Otherwise return the last intersection.
var previous = intervals.first
for (index, element) in intervals.enumerated() {
if index == 0 {
continue
}
previous = previous?.intersection(with: element)
if previous == nil {
break
}
}
return previous
}
Note: Please test with several examples. I tested with above date ranges and its working fine.
What is the most effective way to iterate over a date range in Swift?
If I understand your question correctly, the user will check off some weekdays and provide a duration as a number of days.
Assuming you have the selected weekdays in an array and the duration, you can get the list of matching dates as follows:
// User selected weekdays (1 = Sunday, 7 = Saturday)
var selectedWeekdays = [2, 4, 6] // Example - Mon, Wed, Fri
var duration = 10 // Example - 10 days
let calendar = Calendar.current
var today = Date()
let dateEnding = calendar.date(byAdding: .day, value: duration, to: today)!
var matchingDates = [Date]()
// Finding matching dates at midnight - adjust as needed
let components = DateComponents(hour: 0, minute: 0, second: 0) // midnight
calendar.enumerateDates(startingAfter: today, matching: components, matchingPolicy: .nextTime) { (date, strict, stop) in
if let date = date {
if date <= dateEnding {
let weekDay = calendar.component(.weekday, from: date)
print(date, weekDay)
if selectedWeekdays.contains(weekDay) {
matchingDates.append(date)
}
} else {
stop = true
}
}
}
print("Matching dates = \(matchingDates)")
Swift Range behaving like NSRange and not including endIndex
Range objects Range<T>
in Swift are, by default, presented as a half-open interval [start,end)
, i.e. start..<end
(see HalfOpenInterval IntervalType
).
You can see this if your print your range
variable
let range = Range<Int>(start: 0, end: 2)
print(range) // 0..<2
Also, as Leo Dabus pointed out below (thanks!), you can simplify the declaration of Range<Int>
by using the half-open range operator ..<
directly:
let range = 0..<2
print(range) // 0..<2 (naturally)
Likewise, you can declare Range<Int>
ranges using the closed range operator ...
let range = 0...1
print(range) // 0..<2
And we see, interestingly (in context of the question), that this again verifies that the default representation of Ranges are by means of the half-open operator.
That the half-open interval is default for Range
is written somewhat implicitly, in text, in the language reference for range:
Like other collections, a range containing one element has an endIndex
that is the successor of its startIndex; and an empty range has
startIndex == endIndex.
Range
conforms, however, to CollectionType
protocol. In the language reference to the latter, it's stated clearly that the startIndex
and endIndex
defines a half-open interval:
The sequence view of the elements is identical to the collection view.
In other words, the following code binds the same series of values to
x as does for x in self {}:for i in startIndex..<endIndex {
let x = self[i]
}
To wrap it up; Range
is defined as half-open interval (startIndex ..< endIndex
), even if it's somewhat obscure to find in the docs.
See also
- Swift Language Guide - Basic Operators - Range Operators
Swift includes two range operators, which are shortcuts for expressing
a range of values....
The closed range operator (a...b) defines a range that runs from a to
b, and includes the values a and b. The value of a must not be greater
than b....
The half-open range operator (a..< b) defines a range that runs from a
to b, but does not include b. It is said to be half-open because it
contains its first value, but not its final value.
swift range greater than lower bound
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 thanx
. ...
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 numbersx
, this is the difference betweenx
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!
Swift range check in switch
It is not possible to create a range in Swift that is open on the low end.
This will accomplish what you intended:
switch value {
case 0...f1:
print("one")
case f1...(f1 + f2):
print("two")
case (f1 + f2)...1:
print("three")
default:
print("four")
}
Since switch
matches the first case
it finds, you don't have to worry about being non-inclusive on the low end. f1
will get matched by the "one"
case, so it doesn't matter that the "two"
case also includes it.
If you wanted to make the first case exclude 0
, you could write it like this:
case let x where 0...f1 ~= x && x != 0:
or
case let x where x > 0 && x <= f1:
Related Topics
Iboutlet and Ibaction in Swift
How to Use an Nsattributedstring with a Scrollview in Swiftui
Select() from Linq in Swift 3.0
Urlsession.Shared.Datataskpublisher Not Working on iOS 13.3
Cllocationcoordinate2D Can't Be Instantiated
In Swift 3, What Is a Way to Compare Two Closures
Swift: Print Decimal Precision of Division
In Xcode 6.1. 'Uiimage' Does Not Have a Member Named 'Size' Error
How to Stop Timer in Text View
Navigationview Pops Back to Root, Omitting Intermediate View
Distinction in Swift Between Uppercase "Self" and Lowercase "Self"
Closure Use of Non-Escaping Parameter May Allow It to Escape
Swift Make Method Parameter Mutable
Arkit - How to Export Obj from Iphone/iPad with Lidar
Determining If Swift Dictionary Contains Key and Obtaining Any of Its Values