Home > Mobile >  Swift: EnumeratedSequence: is there a shorter, more colloquial name for this concept?
Swift: EnumeratedSequence: is there a shorter, more colloquial name for this concept?

Time:05-15

A common pattern in getting both an object and it's index when using arrays is:

guard let objectAndIndex = array.enumerated().first(where: {$0.element == foo}) //etc..

The object that is returned by the array.enumerated().first according to apple is an EnumeratedSequence -- is there a shorter, more colloquial term anyone finds useful,

I guess tuple would be a good candidate?

guard let objectTuple = array.enumerated().first(where: {$0.element == foo}) //etc...

I personal don't like thinking or speaking the word tuple but it's at least short.

CodePudding user response:

The object that is returned by the array.enumerated().first according to apple is an EnumeratedSequence

No. The object that is returned by array.enumerated() is an EnumeratedSequence. The object returned by array.enumerated().first is an element of the EnumeratedSequence. That element is indeed a tuple (regardless of what words you may or may not like to say).

Note that this tuple is not, as your names imply, an object and its index. It is an object and its offset. They are not necessarily the same. This is why the elements of the tuple are labeled offset and element.

The reason there is no colloquial name for EnumeratedSequence is that no one in practice ever encounters one — because what you usually do is enumerate the EnumeratedSequence, yielding a series of tuples:

for tuple in "hello".enumerated() {
    print(tuple.offset, "is", tuple.element)
}

CodePudding user response:

A “pair” is a common term for a two-element tuple. In fact, the EnumeratedSequence documentation calls it a pair:

EnumeratedSequence is a sequence of pairs (n, x), where ns are consecutive Int values starting at zero, and xs are the elements of a base sequence.

So maybe you would like objectPair better than objectTuple. Then again, to a native English speaker, objectPair kind of implies two objects of the same type, not an object and an Int. So maybe not.

Perhaps you'd be happier just destructuring the tuple instead of giving it a name:

guard let (offset, object) = array
        .enumerated()
        .first(where: { $0.element == x })
else {
    return
}

If the array elements are also Int, you can explicitly label the tuple members when destructuring so the compiler can verify your assignments:

guard let (offset: offset, element: object) = array
        // ^^^^^^          ^^^^^^^
        // These labels must match the labels returned by enumerated().
        .enumerated()
        .first(where: { $0.element == x })
else {
    return
}

CodePudding user response:

If I were looking for a less cumbersome name for that EnumeratedSequence.Element, I would lean towards a “tuple” ( 1 to both Rob and Matt). But, I generally avoid the naming issue entirely and decompose it into its constituent parts, namely a separate offset and value, e.g.

guard let (offset, value) = collection.enumerated().first(where: { $0.element == searchString }) else {
    return
}

But be careful to not conflate that offset with an index. Specifically, while I am sure it is not the case here, but if you ever start using subsequences (e.g., an array “slice”), an offset produced by enumerated() and the collection’s index may not be the same thing. Consider:

let array = ["a", "b", "c", "d", "e"]
let collection = array[2...]

print(collection)                      // ["c", "d", "e"]
print(collection.indices)              // 2..<5, i.e., [2, 3, 4], not [0, 1, 2] as you might expect

As a result, if your intent was to use that offset as an index, it would not work as you might expect:

let searchString = "e"
guard let (offset, value) = collection.enumerated().first(where: { $0.element == searchString }) else {
    return
}
print(offset)                          // 2
print(value)                           // e
print(collection[offset])              // c !!!

Be careful to not use this “offset” as an “index”. It works for a simple Array, but not for all collections.

It just depends upon for what you are using this offset. If your intent is to retrieve an index that you can later use as a subscript into your collection, it is more prudent to use firstIndex:

guard let index = collection.firstIndex(of: searchString) else { 
    return
}
print(index, collection[index])        // 4 e ... the index can be used to retrieve the value from the collection

Or:

guard let index = collection.firstIndex(where: { $0 == searchString }) else {
    return
}
print(index, collection[index])        // again, 4 e 
  • Related