How can I perform an Array Slice in Swift?
The problem is that mentions[0...3]
returns an ArraySlice<String>
, not an Array<String>
. Therefore you could first use the Array(_:)
initialiser in order to convert the slice into an array:
let first3Elements : [String] // An Array of up to the first 3 elements.
if mentions.count >= 3 {
first3Elements = Array(mentions[0 ..< 3])
} else {
first3Elements = mentions
}
Or if you want to use an ArraySlice
(they are useful for intermediate computations, as they present a 'view' onto the original array, but are not designed for long term storage), you could subscript mentions
with the full range of indices in your else
:
let slice : ArraySlice<String> // An ArraySlice of up to the first 3 elements
if mentions.count >= 3 {
slice = mentions[0 ..< 3]
} else {
slice = mentions[mentions.indices] // in Swift 4: slice = mentions[...]
}
Although the simplest solution by far would be just to use the prefix(_:)
method, which will return an ArraySlice
of the first n elements, or a slice of the entire array if n exceeds the array count:
let slice = mentions.prefix(3) // ArraySlice of up to the first 3 elements
What is a slice in Swift?
The slice points into the array. No point making another array when the array already exists and the slice can just describe the desired part of it.
The addition causes implicit coercion, so it works. To make your assignment work, you would need to coerce:
var list = ["hello", "world"]
var slice: Array<String> = Array(list[0..<list.count])
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.
How to get equal size slice of array literately in Swift
You can iterate your collection indices dropping the last one and return each element along with its subsequent neighbor:
extension Collection {
var neighbors: [SubSequence] {
indices.dropLast().map {
self[$0..<(index($0, offsetBy: 2, limitedBy: self.endIndex) ?? self.endIndex)]
}
}
}
let array = [1, 3, -5, 2, 6]
let chunks = array.neighbors // [[1, 3], [3, -5], [-5, 2], [2, 6]]
let maxValues = chunks.compactMap{$0.max()} // [3, 3, 2, 6]
If you need more than two elements:
extension Collection {
func customChunk(of n: Int) -> [SubSequence] {
indices.dropLast(n-1).map {
self[$0..<(index($0, offsetBy: n, limitedBy: self.endIndex) ?? self.endIndex)]
}
}
}
let chunks3 = array.customChunk(of: 3) // [[1, 3, -5], [3, -5, 2], [-5, 2, 6]]
let maxValues3 = chunks.compactMap{$0.max()} // [3, 3, 6]
How to convert Swift.ArraySlice to Swift.Array?
The actual reason of the error is in this line
lastExp["ArgList"] = (lastExp["ArgList"] as! [String]).dropLast()
You are re-assigning the type ArraySlice
rather then intended Array
so already there you have to create a new array
lastExp["ArgList"] = Array((lastExp["ArgList"] as! [String]).dropLast())
And never ever check strings and collection types for emptiness with .count == 0
. There is an optimized isEmpty
property and don't wrap an if
expression in parentheses in Swift:
if !lastExp.isEmpty { ...
How to slice a string in Swift?
You can split the string and get the first part like this:
var str = "Tower Capital, 99822, Building 2399"
let firstItem = str.componentsSeparatedByString(",").first
Or when using Swift >= 3:
str.components(separatedBy: ",").first
Swift: ArraySlice Array String to [[String]]
You have to (re)create an Array
from the slice
let arr2 = Array(arr1[0...3])
Side note: It's not necessary to annotate types the compiler can infer.
In this case you could proceed without creating an array if the next step accepts a slice.
Is there a swift protocol which Array and ArraySlice both implement
As @MartinR said in the comment, they both conform to RandomAccessCollection
protocol, but you need to use generics in your function due to the fact that RandomAccessCollection
has Self or associated type requirements:
func appendToX<T: RandomAccessCollection>(_ data: T) where T.Element == Double, T.Index == Int {
}
Related Topics
Swift - Firebase Search in Database
How to Avoid Nested Navigation Bars in Swiftui
Could Not Find an Overload for '/' That Accepts the Supplied Arguments
How to Make Keyboard Dismiss When I Press Out of Searchbar on Swift
Nstextfield, Change Text in Swift
Is There a Writetofile Equivalent for Swift 3's 'Data' Type
Swift, How to Play Sound When Press a Button
How to Declare an Inline Function in Swift
Set a Default Value for Uipickerview in Swift
How to Completely Remove Realm Database from iOS
How to Reproduce This Xcode Blue Drag Line
Swift Package Manager - Uikit Dependency
Print() to Console Log with Color
Bold Part of String in Uitextview Swift