Is there any difference at all between suffix(from:) and dropFirst(_:)?
They are completely different.
suffix(from:)
- Defined by the
Collection
protocol. - Returns a
Subsequence
from a given startingIndex
. - Documented time complexity of O(1) (you can see its default implementation here).
- Runtime error if the index you pass is out of range.
- Defined by the
dropFirst(_:)
- Defined by the
Sequence
protocol. - Returns a
SubSequence
with a given maximum number of elements removed from the head of the sequence. - Has a documented time complexity of O(n)*. Although its default implementation actually has a time complexity of O(1), this just postpones the O(n) walk through the dropped elements until iteration.
- Returns an empty subsequence if the number you input is greater than the sequence's length.
- Defined by the
*As with all protocol requirement documented time complexities, it's possible for the conforming type to have an implementation with a lower time complexity. For example, a RandomAccessCollection
's dropFirst(_:)
method will run in O(1) time.
However, when it comes to Array
, these methods just happen to behave identically (except for the handling of out of range inputs).
This is because Array
has an Index
of type Int
that starts from 0
and sequentially counts up to array.count - 1
, therefore meaning that a subsequence with the first n
elements dropped is the same subsequence that starts from the index n
.
Also because Array
is a RandomAccessCollection
, both methods will run in O(1) time.
What is the most succinct way to remove the first character from a string in Swift?
If you're using Swift 3, you can ignore the second section of this answer. Good news is, this is now actually succinct again! Just using String's new remove(at:) method.
var myString = "Hello, World"
myString.remove(at: myString.startIndex)
myString // "ello, World"
I like the global dropFirst()
function for this.
let original = "Hello" // Hello
let sliced = dropFirst(original) // ello
It's short, clear, and works for anything that conforms to the Sliceable protocol.
If you're using Swift 2, this answer has changed. You can still use dropFirst, but not without dropping the first character from your strings characters
property and then converting the result back to a String. dropFirst has also become a method, not a function.
let original = "Hello" // Hello
let sliced = String(original.characters.dropFirst()) // ello
Another alternative is to use the suffix function to splice the string's UTF16View
. Of course, this has to be converted back to a String afterwards as well.
let original = "Hello" // Hello
let sliced = String(suffix(original.utf16, original.utf16.count - 1)) // ello
All this is to say that the solution I originally provided has turned out not to be the most succinct way of doing this in newer versions of Swift. I recommend falling back on @chris' solution using removeAtIndex()
if you're looking for a short and intuitive solution.
var original = "Hello" // Hello
let removedChar = original.removeAtIndex(original.startIndex)
original // ello
And as pointed out by @vacawama in the comments below, another option that doesn't modify the original String is to use substringFromIndex.
let original = "Hello" // Hello
let substring = original.substringFromIndex(advance(original.startIndex, 1)) // ello
Or if you happen to be looking to drop a character off the beginning and end of the String, you can use substringWithRange. Just be sure to guard against the condition when startIndex + n > endIndex - m
.
let original = "Hello" // Hello
let newStartIndex = advance(original.startIndex, 1)
let newEndIndex = advance(original.endIndex, -1)
let substring = original.substringWithRange(newStartIndex..<newEndIndex) // ell
The last line can also be written using subscript notation.
let substring = original[newStartIndex..<newEndIndex]
suffix array Index out of bounds
The problem you are having is that with .suffix
the array does not start with 0. So if you wanted to print the 3rd number in the suffix array, you would have to call print(suffixArray[7]
.
If you read the description for the return value here. It reads:
A subsequence terminating at the end of the collection with at most maxLength elements.
And if you read the description to subsequence:
A collection representing a contiguous subrange of this collection’s elements. The subsequence shares indices with the original collection.
Full example for playground:
let array = [1,2,3,4,5,6,7,8,9,10]
let suffixArray = array.suffix(5) // [6,7,8,9,10]
let prefixArray = array.prefix(5) // [1,2,3,4,5]
var newSuffixArray: [Int] = []
for i in suffixArray {
newSuffixArray.append(i)
}
print(suffixArray[7]) // 8
print(newSuffixArray[2]) // 8
print(prefixArray[2]) // 3
In Swift, what's the cleanest way to get the last two items in an Array?
With Swift 5, according to your needs, you may choose one of the following patterns in order to get a new array from the last two elements of an array.
#1. Using Array's suffix(_:)
With Swift, objects that conform to Collection
protocol have a suffix(_:)
method. Array's suffix(_:)
has the following declaration:
func suffix(_ maxLength: Int) -> ArraySlice<Element>
Returns a subsequence, up to the given maximum length, containing the final elements of the collection.
Usage:
let array = [1, 2, 3, 4]
let arraySlice = array.suffix(2)
let newArray = Array(arraySlice)
print(newArray) // prints: [3, 4]
#2. Using Array
's subscript(_:)
As an alternative to suffix(_:)
method, you may use Array
's subscript(_:)
subscript:
let array = [1, 2, 3, 4]
let range = array.index(array.endIndex, offsetBy: -2) ..< array.endIndex
//let range = array.index(array.endIndex, offsetBy: -2)... // also works
let arraySlice = array[range]
let newArray = Array(arraySlice)
print(newArray) // prints: [3, 4]
Swift: concatenate two arrays without duplicating shared suffix/prefix
Just for fun you can use starts with predicate while iterating your first sequence from the end as follow:
let first: [String] = ["A", "B", "F", "E"]
let second: [String] = ["F", "E", "A", "G"]
var pos = first.endIndex
while pos > first.startIndex,
second.starts(with: first[pos...], by: { $0 != $1}),
!second.isEmpty {
first.formIndex(before: &pos)
}
let result = first[..<pos] + second // ["A", "B", "F", "E", "A", "G"]
This will result in a SubSequence, in this case an array slice. If you need an array just explicitly set the resulting type:
let result: [String] = first[..<pos] + second
Based on OP comments if you need to match the subsequence by pairs just offset every two elements:
let first = "ABFF"
let second = "FFAG"
var pos = first.endIndex
while pos > first.startIndex,
second.starts(with: first[pos...], by: { $0 != $1 }),
!second.isEmpty {
first.formIndex(&pos, offsetBy: -2)
}
let result: String = first[..<pos] + second // "ABFFAG"
If you need the string elements separated by spaces:
var first = "A B C D E F G D E"
var second = "D E F C B A"
first.removeAll(where: \.isWhitespace)
second.removeAll(where: \.isWhitespace)
var pos = first.endIndex
while pos > first.startIndex,
second.starts(with: first[pos...], by: { $0 != $1 }),
!second.isEmpty {
first.formIndex(&pos, offsetBy: -2)
}
let result = (first[..<pos] + second)
.map(String.init)
.joined(separator: " ")
result // "A B C D E F G D E F C B A"
edit/update:
Following the logic shown at your last comment/answer you can do something like:
extension RangeReplaceableCollection where Element: Equatable {
mutating func appendAndMerge<C: Collection>(with collection: C) where C.Element == Element {
var lowerBound = startIndex
formIndex(&lowerBound, offsetBy: Swift.min(count, count-collection.count), limitedBy: endIndex)
while !collection.starts(with: self[lowerBound...]) {
formIndex(&lowerBound, offsetBy: 1, limitedBy: endIndex)
}
replaceSubrange(lowerBound..., with: collection)
}
}
Usage:
var first = ["at", "by", "chicken", "dog", "eat", "for", "good", "dog", "eat"]
let second = ["good", "dog", "eat", "feed", "cats", "bonk", "atrophe"]
first.appendAndMerge(with: second)
print(first)
This will print
["at", "by", "chicken", "dog", "eat", "for", "good", "dog", "eat", "feed", "cats", "bonk", "atrophe"]
Using strings (collection of characters)
var first = "at by chicken dog eat for good dog eat"
let second = "good dog eat feed cats bonk atrophe"
first.appendAndMerge(with: second)
print(first)
This will print:
"at by chicken dog eat for good dog eat feed cats bonk atrophe"
How to determine longest common prefix and suffix for array of strings?
extension Collection where Element: StringProtocol {
func longestCommonPrefix() -> String {
guard var prefix = first.map({ String($0) }) else { return "" }
for string in dropFirst() {
while !string.hasPrefix(prefix) {
prefix.removeLast()
}
}
return prefix
}
func longestCommonSuffix() -> String {
guard var suffix = first.map({ String($0) }) else { return "" }
for string in dropFirst() {
while !string.hasSuffix(suffix) {
suffix.removeFirst()
}
}
return suffix
}
}
print(["A12[1]", "A13[1]", "A14[1]"].longestCommonPrefix()) // "A1"
print(["A12[1]", "A13[1]", "A14[1]"].longestCommonSuffix()) // "[1]"
print(["9-b", "10-b", "11-b"].longestCommonPrefix()) // ""
print(["9-b", "10-b", "11-b"].longestCommonSuffix()) // "-b"
print(["A12", "A14", "A6"].longestCommonPrefix()) // "A"
print(["A12", "A14", "A6"].longestCommonSuffix()) // ""
If you're importing Foundation, you can use its String.commonPrefix(with:)
extension method to write an even shorter version:
import Foundation
extension Collection where Element: StringProtocol {
func longestCommonPrefix() -> String {
guard let first = self.first.map({ String($0) }) else { return "" }
return dropFirst().reduce(first, { $0.commonPrefix(with: $1) })
}
func longestCommonSuffix() -> String {
return String(self.lazy.map({ String($0.reversed()) }).longestCommonPrefix().reversed())
}
}
I learned about commonPrefix(with:)
from Martin R's answer.
Related Topics
Uimarkuptextprintformatter and MAC Catalyst
How to Get The Count of a Type Conforming to 'sequence'
How to Cast from Cftyperef to Axuielement in Swift
Bridging Header for Flurry.H Not Working with Pod
Cannot Convert [Int] to [Int] in Generic Implementation
Navigationlink Inside .Searchable Does Not Work
Swiftui: UIimage (Qrcode) Does Not Load After Calling Function
Difference Between Userdefaults() and Userdefaults.Standard
Dictionary of a Protocol Swift 4
Location Access Request in iOS 11
Can't Load Images on MAC Screensaver Release Build (It Works on Xcode Debug Build)
Implement Protocol with Different Associated Type
Where Do I Register a Valuetransformer in Swift
Make Tabview Background Transparent
What Are The Benefits of an Immutable Struct Over a Mutable One
Extra Argument Userinfo in Call
How to a Convert a Dictionary Slice to a Dictionary in Swift