What Is a Slice in Swift

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



Leave a reply



Submit