What is Object Subscripting
Subscripts are shortcuts for accessing elements from a collection, sequence or list. They are used to set and retrieve values by index without needing separate methods for setting and retrieval. A type can have multiple subscripts and subscripts can have more than one dimension. To access elements via subscripts write one or more values between square brackets after the instance name.
Two example types that use object subscripting are Array
and Dictionary
:
var array = [1, 2, 3, 5, 8, 13]
print(array[0])
print(array[1])
print(array[2])
print(array[3])
print(array[4])
print(array[5])
print(array[1..4]) // 1..4 is the range from 1 to 4 without 4
print(array[1...4]) // 1...4 is the range from 1 to 4 including 4
// prints:
// 1
// 2
// 3
// 5
// 8
// 13
// [2, 3, 5]
// [2, 3, 5, 8]
var dictionary = [
"We": "❤ Swift",
"❤": "Swift",
"Swift": "❤ You"
]
print(dictionary["We"])
print(dictionary["❤"])
print(dictionary["Swift"])
// prints:
// ❤ Swift
// Swift
// ❤ You
How to implement Object Subscripting
The syntax is similar to that of computed properties. You declare a subscript using the subscript
keyword and then specifying one or more inputs and the return type. Subscripts can be read-write or read-only.
The general syntax of a subscript:
class YourSubscriptedClass {
...
subscript (<parameters>) -> <return type> {
// the getter is required
get {
<statements>
}
// the setter is optional
set(<setter name>) {
<statements>
}
}
}
Let’s create a simple subscript that outputs true
if a string contains a substring and false
otherwise.
extension String {
subscript(pattern: String) -> Bool {
get {
let range = self.rangeOfString(pattern)
return !range.isEmpty
}
}
}
var aString = "We ❤ Swift"
aString["❤"] // true
aString["Hello World"] // false
And here we have a similar example that can replace substrings using subscripts:
extension String {
subscript(pattern: String) -> String? {
get {
let range = self.rangeOfString(pattern)
if !range.isEmpty {
return pattern
} else {
return nil
}
}
set(replacement) {
let range = self.rangeOfString(pattern)
if !range.isEmpty {
self = self.stringByReplacingCharactersInRange(range, withString: replacement!)
}
}
}
}
var aString = "We ❤ Swift"
print(aString["❤"]) // ❤
print(aString["Objective-C"]) // nil
aString["We"] = "You"
print(aString) // You ❤ Swift
Note: you can use newValue
instead of replacement
in the setter. newValue
being implicitly declared in setter functions.
extension String {
subscript(pattern: String) -> String? {
...
set {
let range = self.rangeOfString(pattern)
if !range.isEmpty {
self = self.stringByReplacingCharactersInRange(range, withString: newValue!)
}
}
}
}
Usage
You can use object subscripting to make code easy to read. For example Swifter uses subscripting to declare routes in an elegant manner.
Create your own DSL for making queries on CoreData models or implement an XPath-like language for getting elements from your user interface. The sky’s the limit.
As NSHipster said “Here be dragons.”, be careful what crazy things you do with subscripting.
Challenges
- extend the
String
class and add asubscript(pattern: String) -> String?
for pattern matching using regular expressions - implement a matrix class
- implement a chessboard class
If you found this useful please take a moment to share this with your friends
You can discuss this on HN: https://news.ycombinator.com/item?id=7901394