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
"This Class Is Not Key Value Coding-Compliant" Using Coreimage
Quick Sort in Swift Is Giving Error
Emitting a Warning for a Deprecated Swift Protocol Method in Implementing Types
Swift 4 Date Picker Wrong Value Using Minuteinterval
What Was The Reason for Swift Assignment Evaluation to Void
Wkwebview Won't Load (Nsviewcontroller, Os X)
Why Does Cabasicanimation Try to Initialize Another Instance of My Custom Calayer
Mapping Swift Combine Future to Another Future
Issue with Returning a Directory Enumerator from Nsfilemanager Using Enumeratoraturl in Swift
Type 'Bundle' Has No Member "Module"
How to Assign a Generic Function to a Variable
Why This Line Is Not Covered? Xcode Code Coverage
Convert from Nsdictionary to [String:Any]
Sbdata Is Wrong When Sbvalue Comes from a Swift Dictionary
Create a Generic Swift Function to Return an Array of Core Data Entities
Dynamic Dispatching Protocol Extension Doesn't Work Multiple Targets