Negative Arrayslice: Index Is Out of Range

Negative ArraySlice: index is out of range

The reason is that taking an array slice preserves the original
array indices:

let array = [1, 2, 3, 4]
let slice = array[1 ..< 3]

print(slice) // [2, 3]
print(slice.startIndex) // 1
print(slice.endIndex) // 3

print(slice[1]) // 2 (the first element of the slice)
print(slice[0]) // fatal error: Index out of bounds

In your case, after the first call to

input = input[TrackBytes..<input.count]

the first valid index for input is TrackBytes and not 0 and
therefore the next call to

input[0..<TrackBytes]

causes the runtime error.

So the startIndex of a collection is not necessarily zero and you already found a solution, another one is

func readBytes(input: [UInt8]?) {
if let input = input {
var input = input[0..<input.count]
for _ in 0..<NumTracks {
print([UInt8](input.prefix(TrackBytes)))
input = input.suffixFrom(input.startIndex + TrackBytes)
}
}
}

or even shorter, without modifying a local array slice repeatedly:

func readBytes(input: [UInt8]?) {
if let input = input {
for start in 0.stride(to: NumTracks * TrackBytes, by: TrackBytes) {
print([UInt8](input[start ..< start + TrackBytes]))
}
}
}

ArraySlice index out of range in Swift

Try like this:

func arraySliceFunction(array: [String]) {
// Slice the master array with all combinations of airways into separate arrays
// Iterate through the each subArray
for subArray in array.split("") {
// Iterate through each item in subArray
for item in subArray {
print("SLICES COMPONENT: \(item)")
}
}
}

Swift ArraySlice index out of Range for Item[0] in Non-Empty Array in While loop

This works

let texts = ["abcdeb", "abbd", "abde", "abcd", "abcde"]

var start = 0

while start < texts.count {

var newtexts = texts[start..<texts.count] + texts[0..<start]
print("ding", newtexts)
print("dong", newtexts[start]) // Or, you could use Array(newtexts)[0] instead of newtexts[start]
start++
}

Apparently the first value of newTexts cannot be accessed by 0 every time you run the loop. After the loop is run once, newtexts no longer has an element that can be accessed by 0. The second time it is run, it no longer has an element that is accessed by 1, and so on. This is because of the way in which you defined newtexts.

If you create a new array, for example,

var arrayz = texts[1...3] + texts[3...4]
arrayz[0] // Error, Bad instruction...

You get an error over here because this new "array" consists of elements that can be accessed starting from 1. This is similar to what happened in your earlier loop.
The "array" newtexts (or arrayz) that you are creating isn't actually an array; it is of type ArraySlice<String>. This means that it is not accessed how you would a normal array. You will be able to access its elements based on how you defined it.

Hope you understood. :)

How ArraySlice in Swift work internally?

Both Array and ArraySlice are value types, which means that after

var array = [0, 1, 2, 3, 4, 5]
var slice = array[0..<2]

array and slice are independent values, and mutating one does not affect the other:

print(slice) // [0, 1]
array.remove(at: 0)
print(slice) // [0, 1]

How that is achieved is an implementation detail of the Swift standard library,
but one can inspect the source code to get some ideas: At
Array.swift#L1241
we find the implementation of Array.remove(at:):

  public mutating func remove(at index: Int) -> Element {
_precondition(index < endIndex, "Index out of range")
_precondition(index >= startIndex, "Index out of range")
_makeUniqueAndReserveCapacityIfNotUnique()

// ...
}

which uses

  @inlinable
@_semantics("array.make_mutable")
internal mutating func _makeUniqueAndReserveCapacityIfNotUnique() {
if _slowPath(!_buffer.isMutableAndUniquelyReferenced()) {
_copyToNewBuffer(oldCount: _buffer.count)
}
}

Following that trail, we find at ArrayBuffer.swift#L107

  /// Returns `true` iff this buffer's storage is uniquely-referenced.
@inlinable
internal mutating func isUniquelyReferenced() -> Bool {

// ...
}

This isn't the full implementation yet, but (hopefully) already demonstrates that
the (mutating) remove(at:) method copies the element storage to a new
buffer if is was shared (with another array or an array slice).

We can also verify that by printing the element storage base address:

var array = [0, 1, 2, 3, 4, 5]
var slice = array[0..<2]

array.withUnsafeBytes { print($0.baseAddress!) } // 0x0000000101927190
slice.withUnsafeBytes { print($0.baseAddress!) } // 0x0000000101927190

array.remove(at: 0)

array.withUnsafeBytes { print($0.baseAddress!) } // 0x0000000101b05350
slice.withUnsafeBytes { print($0.baseAddress!) } // 0x0000000101927190

The same “copy-on-write” technique is used if arrays, dictionaries, or strings
are copied, or if String and Substring share storage.

So an array slice shares the element storage with its originating
array as long as neither of them is mutated.

That is still a useful feature. Here is a simple example:

let array = [1, 4, 2]
let diffs = zip(array, array[1...]).map(-)
print(diffs) // [-3, 2]

array[1...] is a view/slice of the given array, without actually copying
the elements.

A recursive binary search function where slices (of the left or right half) are passed down would be another application.

Empty array when index is end of array

I put in {ix} for the positions instead of commas.

a = [{ix0}0{ix1}1{ix2}2{ix3}3{ix4}4{ix5}]

a[5,0] means go to 5 {ix5} and take 0 elements. That is just an empty array.

{ix6} is not a valid starting positions, thus nil.

Is there a better way to get a possibly-empty array slice/range from some index to the end?

However, I'm wondering: is there a better solution that I'm missing?

Upgrade to 3.0. This was a bug in 2.7 that is fixed there.

The way this works now is that, if only one of the indices in a range is negative, it'll always be interpreted as the larger of the two, so with a negative end, iteration is forced to go forward.

If the index isn't actually larger, this results in no elements instead, which is precisely what you want here.

How to copy end of the Array in swift?

And another one ...

let array = [1, 2, 3, 4, 5]
let fromIndex = 2
let endOfArray = array.dropFirst(fromIndex)
print(endOfArray) // [3, 4, 5]

This gives an ArraySlice which should be good enough for most
purposes. If you need a real Array, use

let endOfArray = Array(array.dropFirst(fromIndex))

An empty array/slice is created if the start index is larger than (or equal to) the element count.

Understanding slicing

The syntax is:

a[start:stop]  # items start through stop-1
a[start:] # items start through the rest of the array
a[:stop] # items from the beginning through stop-1
a[:] # a copy of the whole array

There is also the step value, which can be used with any of the above:

a[start:stop:step] # start through not past stop, by step

The key point to remember is that the :stop value represents the first value that is not in the selected slice. So, the difference between stop and start is the number of elements selected (if step is 1, the default).

The other feature is that start or stop may be a negative number, which means it counts from the end of the array instead of the beginning. So:

a[-1]    # last item in the array
a[-2:] # last two items in the array
a[:-2] # everything except the last two items

Similarly, step may be a negative number:

a[::-1]    # all items in the array, reversed
a[1::-1] # the first two items, reversed
a[:-3:-1] # the last two items, reversed
a[-3::-1] # everything except the last two items, reversed

Python is kind to the programmer if there are fewer items than you ask for. For example, if you ask for a[:-2] and a only contains one element, you get an empty list instead of an error. Sometimes you would prefer the error, so you have to be aware that this may happen.

Relationship with the slice object

A slice object can represent a slicing operation, i.e.:

a[start:stop:step]

is equivalent to:

a[slice(start, stop, step)]

Slice objects also behave slightly differently depending on the number of arguments, similarly to range(), i.e. both slice(stop) and slice(start, stop[, step]) are supported.
To skip specifying a given argument, one might use None, so that e.g. a[start:] is equivalent to a[slice(start, None)] or a[::-1] is equivalent to a[slice(None, None, -1)].

While the :-based notation is very helpful for simple slicing, the explicit use of slice() objects simplifies the programmatic generation of slicing.



Related Topics



Leave a reply



Submit