[swift-evolution] [Idea] Add an (Index, Element) sequence to CollectionType

Wallacy wallacyf at gmail.com
Tue Dec 29 10:06:43 CST 2015


FWIW: In cases like this, just remember call ".reverse()" and remove from
the back.

var array = [ "", "a", "b", "c", "", "d" ]
for (index, element) in array.enumerate().reverse() {
    if element == "" {
        array.removeAtIndex(index)
    }
}
print(array) // ["a", "b", "c", "d"]

zip is useful when we have ArraySlice as has been said before:

var array = [ "", "a", "b", "c", "", "d", "", "e", "" , "f", "g", ""]
var secondHalf = array[array.count/2..<array.count]
print(secondHalf) // ["", "e", "", "f", "g", ""]
for (index, element) in zip(secondHalf.indices, secondHalf).reverse() {
    if element == "" {
        secondHalf.removeAtIndex(index)
    }
}

print(secondHalf) // ["e", "f", "g"]

Anyway, it would not be correct to ".enumerate()" returns (Index, Element)
instead of (n, Element)?

I believe that the current behavior was thought when Slices had indices
starting with zero.

Em ter, 29 de dez de 2015 às 00:30, Dany St-Amant via swift-evolution <
swift-evolution at swift.org> escreveu:

>
> The original example contains a bug which is present on all looping
> version/alternative due to the mutating nature of the array. Using the zip
> implementation:
>
> var array = [ "", "a", "b", "c", "", "d" ]
> var secondHalf = array[array.count/2..<array.count]
> for (index, element) in zip(secondHalf.indices, secondHalf) {
>     if element == "" {
>         secondHalf.removeAtIndex(index)
>     }
> }
>
> The variable index cycles through 3,4 and 5; but in order to be able to
> remove the right element beyond the first removal, the variable index
> should have cycled through 3, 4 and 4 (as some elements got shifted after
> the first mutation). Mutating the array/list which one loops over is a
> risky business and is a nice source of bugs (including infinite loop). If
> this (Index, Element) is further investigated, it should consider that one
> may also want to do a insert(:atIndex:), and may expect the (Index,
> Element) to have proper Index but only for the original Element.
>
> Dany St-Amant
>
>
> Le 28 déc. 2015 à 01:06, Kevin Ballard via swift-evolution <
> swift-evolution at swift.org> a écrit :
>
> What you're asking for can already be done with `zip(col.indices, col)`.
> And in my experience the need for this sort of thing is rare enough that
> there's no need to have a dedicated property for it in the stdlib. The few
> times that I've needed this sort of thing, I've always just said
>
> for index in col.indices {
>     let elt = col[index]
>     // ...
> }
>
> and that's pretty simple. But if I ever did need to map it, I'd just use
> the aforementioned zip() expression.
>
> -Kevin Ballard
>
> On Sun, Dec 27, 2015, at 12:08 AM, Patrick Pijnappel via swift-evolution
> wrote:
>
> -- Introduction
>
> There should be a property on CollectionType that returns a sequence of
> (Index, Element) tuples.
> Currently enumerate() is often used instead, but it is not well suited to
> the task and can lead to bugs.
>
>
>
> -- Motivation
>
> Using enumerate() instead of an (Index, Element) sequence has two main
> problems.
> Both arise because enumerate() returns a sequence of (n, Element) tuples,
> where n is the element *number*, instead of a sequence of (Index, Element)
> .
>
> 1) It doesn't work for collections not indexed by integers.
>
> 2) It doesn't do what you might expect in some cases, as indices do not
> always start at 0.
> For example ArraySlice's indices do not: array[2..<5] starts with index 2.
> Consider the following code to take the 2nd half of the array and remove
> all empty elements:
>
> var array = [ "", "a", "b", "c", "", "d" ]
> var secondHalf = array[array.count/2..<array.count]
> for (index, element) in secondHalf.enumerate() {
> if element == "" {
> secondHalf.removeAtIndex(index)
> }
> }
>
> This code will crash (ignoring for a moment this should probably be using
> filter).
>
>
>
> -- Alternatives
>
> The same effect can already be achieved using the following:
>
> for index in collection.indices {
>   let element = collection[index]
>   // ...
> }
>
> However having a dedicated (Index, Element) sequence has the following
> advantages:
> a) It can help prevent people from using enumerate() inappropriately.
> b) It is very common use case that deserves shortening.
> c) It can be chained (e.g. to map).
>
>
>
> -- Proposed Solution
>
> Add a property/method on CollectionType that returns a sequence of (Index,
> Element) tuples.
> For example, using a property named indexed:
>
> for (index, element) in collection.indexed {
>   // ...
> }
>
> This should be the preferred idiom when you want both the index and the
> element.
>
> Note that enumerate() does still have valid roles to play:
> - When you actually do want the element number, not the index.
> - When you have a SequenceType, as it isn't indexed.
>
>
>
> -- Implementation
>
> The feature could be entirely implemented using existing constructs:
>
> extension CollectionType {
>   var indexed: AnySequence<(Index, Generator.Element)> {
>     return AnySequence(indices.lazy.map { ($0, self[$0]) })
>   }
> }
>
> Alternatively, a dedicated SequenceType and/or GeneratorType could be
> added.
>
>
> *_______________________________________________*
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20151229/83f60a16/attachment.html>


More information about the swift-evolution mailing list