0
let smallRange = 1..<2
let largeRange = 0..<10
largeRange ~= smallRange // False

Why does this compile at the first place!?
But if it compiles the answer should be True

The next code works:

smallRange.clamped(to: largeRange) == smallRange // True

Is there any simpler way to check?

Dávid Pásztor
  • 40,247
  • 8
  • 59
  • 80
Roman
  • 1,097
  • 9
  • 13

4 Answers4

3

I don't think you will find a "simpler" or "shorter" way of writing this but you can define your own pattern matching operator to shorten your code:

extension Range {
    static func ~=(lhs: Self, rhs: Self) -> Bool {
        rhs.clamped(to: lhs) == rhs
    }
}

extension ClosedRange {
    static func ~=(lhs: Self, rhs: Self) -> Bool {
        rhs.clamped(to: lhs) == rhs
    }
}

let smallRange = 1..<2
let largeRange = 0..<10
smallRange ~= largeRange // false
largeRange ~= smallRange // true

Note: It compiles because there is a generic implementation used to check for equality like switching a string therefore it would only return true if both ranges were the same.

func ~= <T>(a: T, b: T) -> Bool where T : Equatable
Leo Dabus
  • 198,248
  • 51
  • 423
  • 494
  • Won't this overriding of ~= impact a switching behaviour of ranges? Maybe it's safer instead of ~= override "contains". – Roman Sep 26 '20 at 23:22
  • This is not generic so it will only apply to ranges – Leo Dabus Sep 26 '20 at 23:24
  • 2
    You are the only one who answered why it compiles :) Every one else just ignored this question)) – Roman Sep 26 '20 at 23:24
  • 1
    @Roman if you need to switch a range and check different ranges it will match all ranges that fit within the large range. I have never needed to switch a range to check if other ranges are equal to it – Leo Dabus Sep 26 '20 at 23:35
  • 1
    @Roman Actually is the opposite if you switch a small range it will match all larger ranges that contains it. – Leo Dabus Sep 27 '20 at 02:45
2

You seem to have a working formula. So why not just use it? Problem solved. If, however, you think your working formula is too long, hard to remember, whatever, then simply refactor it into an extension so you can shorten it:

extension Range {
    func embraces(_ other: Self) -> Bool {
        return other.clamped(to: self) == other
    }
}

Now you can say

largeRange.embraces(smallRange) // true

That's shorter. Simpler. Whatever.

That's (part of) what programming is. If you don't like the language, change it.

matt
  • 447,615
  • 74
  • 748
  • 977
  • 1
    My philosophy here is identical to https://stackoverflow.com/questions/24034544/dispatch-after-gcd-in-swift/24318861#24318861. Sure, `asyncAfter(deadline:now()+n)` is easy to say. But `delay(n)` is even easier, so why not change the language so you can say it? And clearly the world agrees, since just about everyone _does_ say it. I have a library of code snippets that I can embed into any project so I can talk the way I want to. (Here are some of them: https://github.com/mattneub/Programming-iOS-Book-Examples/blob/master/bk2ch28AppendixB/bk2ch40AppendixB/ViewController.swift) – matt Sep 25 '20 at 15:45
1

You can compare the lowerBound and upperBound of the 2 ranges (or use the contains function to check that one range contains both bounds of the other range) to learn if one is a subset of the other.

extension Range {
    func contains(otherRange: Range) -> Bool {
        lowerBound <= otherRange.lowerBound && upperBound >= otherRange.upperBound
    }
}

let smallRange = 1..<2
let middleRange = 5..<8
let largeRange = 0..<10

smallRange.contains(otherRange: middleRange) // false
middleRange.contains(otherRange: smallRange) // false
largeRange.contains(otherRange: middleRange) // true

The other solution using contains:

extension Range {
    func contains(otherRange: Range) -> Bool {
        contains(otherRange.lowerBound) && contains(otherRange.upperBound)
    }
}
Dávid Pásztor
  • 40,247
  • 8
  • 59
  • 80
0

Well, if one range is between another, it means that the edges are between each other So, in your example, it would be largeRange.first < smallRange.first && largeRange.last > smallRange.last

Pastre
  • 198
  • 2
  • 9