Access Tuples by Subscript

Access tuples by subscript

What you want is not possible with tuples and if you dont want to cast everything later-on your only option is struct or class. struct seems like a better choise :)

struct MyStruct {
let opt1 = 0
let opt2 = 0
let opt3 = 0
...
let boolThing = false
}

Can a Swift subscript implementation set a tuple value?

Yes, a subscript method can have a tuple type.

First, you have defined the subscript value as an optional
(Foo, Bar)?, so newValue needs to be unwrapped. (And you have to
decide what to do if newValue is nil.)

Second, if you want
to access the tuple member by names .foo/.bar, you have to define a named tuple:

subscript(index: Int) -> (foo: Foo, bar: Bar)? {
// ...

set(newValue) {
if let value = newValue {
foos[index] = value.foo
bars[index] = value.bar
}
}
}

Alternatively, access the tuple members with value.0 and value.1.

Why can we not access elements of a tuple by index?

Because [] is an operator (named operator[]), thus a member function, and is called at run-time.

Whereas getting the tuple item is a template mechanism, it must be resolved at compile time. Which means this can be only done with the <> templating syntax.

To better understand, a tuple may store different types. A template function may return different types depending on the index passed, as this is resolved at compile time.
The operator[] must return a unique type, whatever the value of the passed parameter is. Thus the tuple functionality is not achievable.

get<0>(x) and get<1>(x) are two different functions generated at compile time, and return different types. The compiler generates in fact two functions which will be mangled to something like

int get_tuple_int_string_int_0(x)

and

string get_tuple_int_string_int_1(x)

Subscript tuple-unpacking notation in comprehensions

No, you cannot slice an unpacking operation.

There are various possible workarounds though:

  • If the iterable you're unpacking can be sliced, just slice it directly. For example, to get the 3rd element of a range:

    >>> [*range(4)[2:3]]
    [2]

    So why are we using [2:3] here? It's simple: range(4)[2] would return a single integer, and integers can't be unpacked:

    >>> [*range(4)[2]]
    Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    TypeError: 'int' object is not iterable

    But range(4)[2:3] returns a single-element list, so the unpacking succeeds.

  • If the iterable can't be sliced, slice it anyway by using itertools.islice:

    >>> [*itertools.islice(zip([1, 2, 3], [4, 5, 6]), 2, 3)]
    [(3, 6)]
  • If you only want a single element of a slice-able iterable, just don't unpack the iterable at all:

    >>> [range(4)[2]]
    [2]

Swift: Get an element from a tuple

According to the documentation (scroll down to Tuples), there are three ways to do it.

Given

var answer: (number: Int, good: Bool) = (100, true)

Method 1

Put the element variable name within a tuple.

let (firstElement, _) = answer
let (_, secondElement) = answer

or

let (firstElement, secondElement) = answer

Method 2

Use the index.

let firstElement = answer.0
let secondElement = answer.1

Method 3

Use the names. This only works, of course, if the elements were named in the Tuple declaration.

let firstElement = answer.number
let secondElement = answer.good

How to access a field of a namedtuple using a variable for the field name?

You can use getattr

getattr(my_car, field)


Related Topics



Leave a reply



Submit