8

How does subscripting a lazy filter work?

let ary = [0,1,2,3]
let empty = ary.lazy.filter { $0 > 4 }.map { $0 + 1 }
print(Array(empty)) // []
print(empty[2])     // 3

It looks like it just ignores the filter and does the map anyway. Is this documented somewhere? What other lazy collections have exceptional behavior like this?

Max
  • 21,123
  • 5
  • 49
  • 71

2 Answers2

8

It comes down to subscripting a LazyFilterCollection with an integer which in this case ignores the predicate and forwards the subscript operation to the base.

For example, if we're looking for the strictly positive integers in an array :

let array = [-10, 10, 20, 30]
let lazyFilter = array.lazy.filter { $0 > 0 }

print(lazyFilter[3])                 // 30

Or, if we're looking for the lowercase characters in a string :

let str = "Hello"
let lazyFilter = str.lazy.filter { $0 > "Z" }

print(lazyFilter[str.startIndex])    //H

In both cases, the subscript is forwarded to the base collection.

The proper way of subscripting a LazyFilterCollection is using a LazyFilterCollection<Base>.Index as described in the documentation :

let start = lazyFilter.startIndex
let index = lazyFilter.index(start, offsetBy: 1)
print(lazyFilter[index])  

Which yields 20 for the array example, or l for the string example.


In your case, trying to access the index 3:

let start = empty.startIndex
let index = empty.index(start, offsetBy: 3)
print(empty)

would raise the expected runtime error :

Fatal error: Index out of range

ielyamani
  • 17,807
  • 10
  • 55
  • 90
2

To add to Carpsen90's answer, you run into one of Collection's particularities: it's not recommended, nor safe to access collections by an absolute index, even if the type system allows this. Because the collection you receive might be a subset of another one.

Let's take a simpler example, array slicing:

let array = [0, 1, 2, 3, 4]
let slice = array[2..<3]
print(slice) // [2]
print(slice.first) // Optional(2)
print(slice[0]) // crashes with array index out of bounds

Even if slice is a collection indexable by an integer, it's still unsafe to use absolute integers to access elements of that collection, as the collection might have a different set of indices.

Cristik
  • 30,989
  • 25
  • 91
  • 127
  • That makes sense, but it makes me wonder why they didn't go the route of strings and completely disallow int subscripts. – Max Jan 22 '19 at 12:58
  • @Max, maybe for ease of access, and to avoid awkward constructs when trying to randomly access elements from arrays. Almost all languages allow array indexing by integers. – Cristik Jan 22 '19 at 13:05