"Collection corresponding..." error on Swift 3 upgrade - swift

Here's a pre-Xcode-8 Swift call ..
func gappizeAtDoubleNewlines()
{
let t = self.text!
var index = t.startIndex
var follow = index.advancedBy(1)
for i in 0 ..< (t.characters.count-4)
{
let r = index ... follow
if ( t.substringWithRange(r) == "\n\n" )
{ alterLineGapHere(i) }
index = index.advancedBy(1)
follow = index.advancedBy(1)
}
}
using the automatic upgrade to Swift3, I got these errors...
in text,
func gappizeAtDoubleNewlines()
{
let t = self.text!
var index = t.startIndex
var follow = <#T##Collection corresponding to `index`##Collection#>.index(index, offsetBy: 1)
for i in 0 ..< (t.characters.count-4)
{
let r = index ... follow
if ( t.substring(with: r) == "\n\n" )
{ alterLineGapHere(i) }
index = <#T##Collection corresponding to `index`##Collection#>.index(index, offsetBy: 1)
follow = <#T##Collection corresponding to `index`##Collection#>.index(index, offsetBy: 1)
}
}
What is the solution in Swift3 ??

See SE-0065: 'Collections move their indices' – in this case you can just replace the editor placeholders with t:
func gappizeAtDoubleNewlines() {
let t = self.text!
var index = t.startIndex
// Note that because substring(by:) takes a Range<String.Index>, rather than
// a ClosedRange, we have to offset the upper bound by one more.
var follow = t.index(index, offsetBy: 2)
for i in 0 ..< (t.characters.count-4) {
let r = index ..< follow
if (t.substring(with: r) == "\n\n") {
alterLineGapHere(i)
}
index = t.index(index, offsetBy: 1)
follow = t.index(follow, offsetBy: 1)
}
}
Although note that String isn't a Collection itself, it just implements some convenience methods for indexing that forward to t.characters, which is a Collection.

Related

CodingBat string_bits problem solved using swit for loop

Question:
Given a string, return a new string made of every other char starting with the first, so "Hello" yields "Hlo".
string_bits('Hello') → 'Hlo'
string_bits('Hi') → 'H'
string_bits('Heeololeo') → 'Hello'
Solution:
func string_bits(userString: String) ->String{
var myString = ""
for(i, v) in userString.enumerated(){
if i % 2 == 0{
myString.append(v)
}
}
return myString
}
Output: Hello
Now my question:
Is there any I can iterate my index any way in swift like object-c, c, or other programming languages does. For instance:
result = ""
# On each iteration, add the substring of the chars 0..i
for i in range(len(str)):
result = result + str[:i+1]
return result
str[:i+1]
Here, I am adding +1 with the current index and getting the index value. How can I do this in swift.
extension Collection {
func everyNthIndex(n: Int) -> UnfoldSequence<Index,Index> {
sequence(state: startIndex) { index in
guard index < endIndex else { return nil }
defer { index = self.index(index, offsetBy: n, limitedBy: endIndex) ?? endIndex }
return index
}
}
}
let alphabet = "abcdefghijklmnopqrstuvwxyz"
for evenIndex in alphabet.everyNthIndex(n: 2) {
print("evenIndex", evenIndex, "char:", alphabet[evenIndex])
}
for oddIndex in alphabet.dropFirst().everyNthIndex(n: 2) {
print("oddIndex", oddIndex, "char:", alphabet[oddIndex])
}
regular approach using while loop:
var index = alphabet.startIndex
while index < alphabet.endIndex {
defer { index = alphabet.index(index, offsetBy: 1) }
print(alphabet[index])
print(index)
}
or enumerating the string indices:
func string_bits(userString: String) -> String {
var myString = ""
for (offset,index) in userString.indices.enumerated() {
if offset.isMultiple(of: 2) {
myString.append(userString[index])
}
}
return myString
}

How to solve a problem with using the method of branches and borders?

All words of the ternary language consist of only 3 letters: a, b, and c and all have a strictly specified length N. Words that do not contain two identical subsequences of letters in a row are considered correct. For example, abcacb is the correct word, and ababc is not the correct one, since the ab subsequences go there.
I tried to solve the problem with a complete enumeration of all possible combinations and a function that looked for a repeating sequence. However, this turned out to be the wrong decision. The problem needs to be solved somehow using the branch and bound method. I have absolutely no idea how this problem can be solved by this method. I would be very happy if someone provides examples or explains to me. I have already spent six days to solve this problem and am very tired.
My wrong solution:
import Foundation
func findRepetition(_ p: String) -> [String:Int] {
var repDict: [String:Int] = [:]
var p = p
while p.count != 0 {
for i in 0...p.count-1 {
repDict[String(Array(p)[0..<i]), default: 0] += 1
}
p = String(p.dropFirst())
}
return repDict
}
var correctWords = [String]()
var wrongWords = [String]()
func getRepeats(_ p: String) -> Bool {
let p = p
var a = findRepetition(p)
for i in a {
var substring = String(Array(repeating: i.key, count: 2).joined())
if p.contains(substring) {
wrongWords.append(p)
return false
}
}
correctWords.append(p)
return true
}
var counter = 0
func allLexicographicRecur (_ string: [String.Element], _ data: [String], _ last: Int, _ index: Int){
var length = string.count-1
var data = data
for i in 0...length {
data[index] = String(string[i])
if index == last {
if getRepeats(data.joined()) {
counter += 1
}
}else{
allLexicographicRecur(string, data, last, index+1)
}
}
}
func threeLanguage(_ l: Int) {
var alphabet = "abc"
var data = Array(repeating: "", count: l)
allLexicographicRecur(alphabet.sorted(), data, l-1, 0)
print("The specified word length: \(l), the number of correct words: \(counter)\n")
print("Correct words:\n\(correctWords)\n")
print("Wrong words:\n\(wrongWords)")
}
threeLanguage(3)
Example:
abca is the right word.
abab is wrong (ab).
aaaa is also wrong (a).
abcabc is also incorrect (abc).
If I correctly understood your problem, you need to separate you input string to parts N-length and check parts by your rules. Smth like this
let constant: Int = 3
extension String {
private func components(withLength length: Int) -> [String] {
return stride(from: 0, to: count, by: length).map {
let start = index(startIndex, offsetBy: $0)
let end = index(start, offsetBy: length, limitedBy: endIndex) ?? endIndex
return String(self[start ..< end])
}
}
var numberOfValidWords: Int {
var numberOfIncorrectWords = 0
let length = count - constant
let array = components(withLength: constant)
for component in array {
let computedLength = replacingOccurrences(of: component, with: "").count
if computedLength != length {
print("as is lengths are not equal, this part is met in string several times")
numberOfIncorrectWords += 1
continue
}
}
return array.count - numberOfIncorrectWords
}
}
Hope it will be helpful

Find first matching object in array given starting index

I have an array of objects
var arrayOfObjects: [Object]?
And they all have a property called depth. I want to find the very next object in that array that has the same depth with a specific object I know the index of:
[
...objects_before...,
object_I_know: {depth:3},
...objects_after...
]
Is there a more efficient way other than using a for loop starting from the object_I_know index, and traversing down until it finds one?
let nextIndex: Int? = (givenIndex ..< array.endIndex).first { index in
return array[index].depth == array[givenIndex].depth
}
The item with the object with the same depth would be at that nextIndex if there is one
let nextObject: Object? = (nextIndex == nil) ? nil : array[nextIndex!]
Here's a sample model I came up with for testing:
struct S {
let id: Int
let depth: Int
}
var id = 0
let getID: () -> Int = { defer { id += 1 }; return id }
let objects = [
S(id: getID(), depth: 1),
S(id: getID(), depth: 3),
S(id: getID(), depth: 2),
S(id: getID(), depth: 3),
S(id: getID(), depth: 4),
]
Here's a solution that account for the situations in which there are no elements which match the predicate, or only 1 such element:
let isDepth3: (S) -> Bool = { $0.depth == 3 }
// Get the index of the first item (can be nil)
let indexOfFirstDepth3 = objects.index(where: isDepth3)
// Get the index after that (can be nil), so that we can exclude everything before it
let firstIndexOfRemainingItems = indexOfFirstDepth3.flatMap { objects.index($0, offsetBy: +1, limitedBy: objects.endIndex) }
let indexOfSecondDepth3 = firstIndexOfRemainingItems.flatMap {
// Slice the `objects` array, to omit all the items before up to and including the first depth 3 item.
// Then find the index of the next next 3 item thereafter.
return objects[$0...].index(where: isDepth3)
}
// Print results
func stringifyOptional<T>(_ item: T?) -> String {
return item.map{ String(describing: $0) } ?? "nil"
}
print("First item with depth 3 is \(stringifyOptional(indexOfFirstDepth3.map{ objects[$0] })) at index \(stringifyOptional(indexOfFirstDepth3))")
print("Second item with depth 3 is \(stringifyOptional(indexOfSecondDepth3.map{ objects[$0] })) at index \(stringifyOptional(indexOfFirstDepth3))")
If you're sure that you'll have 2 such elements, and you're sure that force unwrapping will be safe, then this can be simplified dramatically:
let isDepth3: (S) -> Bool = { $0.depth == 3 }
let indexOfFirstDepth3 = objects.index(where: isDepth3)!
let indexOfSecondDepth3 = objects[indexOfFirstDepth3...].index(where: isDepth3)!
// Just printing the result
print("First item with depth 3 is \(objects[indexOfFirstDepth3]) at index \(indexOfFirstDepth3)")
print("Second item with depth 3 is \(objects[indexOfFirstDepth3])) at index \(indexOfFirstDepth3)")
Context
struct DepthObject { let depth: Int }
let objs = [a, b, c, d ,e]
let index = 1 //predetermined index
let depthToFind = objs[index].depth
let startIndex = index + 1
let remainingArray = objs[startIndex...] //The slice we want to work with
One way
let aMessage: String? = remainingArray
.first { $0.depth == depthToFind }
.flatMap { "The world is yours \($0)" }
Decide based on it
if let nextDepthObject = remainingArray.first(where: { $0.depth == depthToFind }) {
//Found the next one!
} else {
//Didn't find it!
}
Loop it
var nextDepthObject: DepthObject? = nil
for sliceDepthObject in remainingArray {
if sliceDepthObject.depth == depthToFind {
nextDepthObject = sliceDepthObject
break
}
}
Implementing a particular approach
func nextDepthObject(within array: Array<DepthObject>, startingAt index: Int) -> DepthObject? {
guard index + 1 < array.count && index < array.count else {
return nil
}
let depthToFind = array[index].depth
let suffixArray = array[(index + 1)...]
return suffixArray.first { $0.depth == depthToFind }
}
let theNextOne: DepthObject? = nextDepthObject(within: objs, startingAt: index)
You can add an extension over Collection (which Array conforms to):
extension Collection {
func next(startingWith next: Self.Index, where match: (Element) -> Bool) -> Element? {
guard next < endIndex else { return nil }
return self[next..<endIndex].first(where: match)
}
}
You'd use it like this:
let nextMatch = arrayOfObjects.next(startingWith: foundIndex+1) { $0.depth == searchedDepth }
class Object {
var name: String
var depth: Float
init(name: String, depth: Float) {
self.name = name
self.depth = depth
}
}
let o1 = Object(name: "object1", depth: 10)
let o2 = Object(name: "object2", depth: 12)
let o3 = Object(name: "object3", depth: 4)
let o4 = Object(name: "object4", depth: 12)
let o5 = Object(name: "object5", depth: 14)
let array = [o1, o2, o3, o4, o5]
let knownIndex = 1
let knownDepth = array[knownIndex].depth
var searchResults = [Object]()
// iterate through the second half of the array after the known
// index and break the loop when a match is found
for i in knownIndex + 1..<array.count {
if array[i].depth == knownDepth {
searchResults = [array[i]]
break
}
}
// after the loop is finished (either by going all the way to the
// end or breaking after a match is found), check your search results
if searchResults.count > 0 {
print("match found: \(searchResults[0].name)")
} else {
print("no match found")
}
index(where:) uses a loop also, unbeknownst to the commenter, except that the compiler does it for you behind the scenes. index(where:) also loops through the entire array which is not very efficient if you already know the starting index (which OP does).

In Swift, how to modify a character in string with subscript?

Like in C, we can simply do
str[i] = str[j]
But how to write the similar logic in swift?
Here is my code, but got error:
Cannot assign through subscript: subscript is get-only
let indexI = targetString.index(targetString.startIndex, offsetBy: i)
let indexJ = targetString.index(targetString.startIndex, offsetBy: j)
targetString[indexI] = targetString[indexJ]
I know it may work by using this method, but it's too inconvenient
replaceSubrange(, with: )
In C, a string (char *) can be treated as an array of characters. In Swift, you can convert the String to an [Character], do the modifications you want, and then convert the [Character] back to String.
For example:
let str = "hello"
var strchars = Array(str)
strchars[0] = strchars[4]
let str2 = String(strchars)
print(str2) // "oello"
This might seem like a lot of work for a single modification, but if you are moving many characters this way, you only have to convert once each direction.
Reverse a String
Here's an example of a simple algorithm to reverse a string. By converting to an array of characters first, this algorithm is similar to the way you might do it in C:
let str = "abcdefg"
var strchars = Array(str)
var start = 0
var end = strchars.count - 1
while start < end {
let temp = strchars[start]
strchars[start] = strchars[end]
strchars[end] = temp
start += 1
end -= 1
}
let str2 = String(strchars)
print(str2) // "gfedcba"
Dealing with String with Swift is major pain in the a**. Unlike most languages I know that treat string as an array of characters, Swift treats strings as collection of extended grapheme clusters and the APIs to access them is really clumsy. Changes are coming in Swift 4 but that manifesto lost me about 10 paragraphs in.
Back to your question... you can replace the character like this:
var targetString = "Hello world"
let i = 0
let j = 1
let indexI = targetString.index(targetString.startIndex, offsetBy: i)
let indexJ = targetString.index(targetString.startIndex, offsetBy: j)
targetString.replaceSubrange(indexI...indexI, with: targetString[indexJ...indexJ])
print(targetString) // eello world
I was quite shocked as well by the fact that swift makes string indexing so damn complicated. For that reason, I have built some string extensions that enable you to retrieve and change parts of strings based on indices, closed ranges, and open ranges, PartialRangeFrom, PartialRangeThrough, and PartialRangeUpTo. You can download the repository I created here
You can also pass in negative numbers in order to access characters from the end backwards.
public extension String {
/**
Enables passing in negative indices to access characters
starting from the end and going backwards.
if num is negative, then it is added to the
length of the string to retrieve the true index.
*/
func negativeIndex(_ num: Int) -> Int {
return num < 0 ? num + self.count : num
}
func strOpenRange(index i: Int) -> Range<String.Index> {
let j = negativeIndex(i)
return strOpenRange(j..<(j + 1), checkNegative: false)
}
func strOpenRange(
_ range: Range<Int>, checkNegative: Bool = true
) -> Range<String.Index> {
var lower = range.lowerBound
var upper = range.upperBound
if checkNegative {
lower = negativeIndex(lower)
upper = negativeIndex(upper)
}
let idx1 = index(self.startIndex, offsetBy: lower)
let idx2 = index(self.startIndex, offsetBy: upper)
return idx1..<idx2
}
func strClosedRange(
_ range: CountableClosedRange<Int>, checkNegative: Bool = true
) -> ClosedRange<String.Index> {
var lower = range.lowerBound
var upper = range.upperBound
if checkNegative {
lower = negativeIndex(lower)
upper = negativeIndex(upper)
}
let start = self.index(self.startIndex, offsetBy: lower)
let end = self.index(start, offsetBy: upper - lower)
return start...end
}
// MARK: - Subscripts
/**
Gets and sets a character at a given index.
Negative indices are added to the length so that
characters can be accessed from the end backwards
Usage: `string[n]`
*/
subscript(_ i: Int) -> String {
get {
return String(self[strOpenRange(index: i)])
}
set {
let range = strOpenRange(index: i)
replaceSubrange(range, with: newValue)
}
}
/**
Gets and sets characters in an open range.
Supports negative indexing.
Usage: `string[n..<n]`
*/
subscript(_ r: Range<Int>) -> String {
get {
return String(self[strOpenRange(r)])
}
set {
replaceSubrange(strOpenRange(r), with: newValue)
}
}
/**
Gets and sets characters in a closed range.
Supports negative indexing
Usage: `string[n...n]`
*/
subscript(_ r: CountableClosedRange<Int>) -> String {
get {
return String(self[strClosedRange(r)])
}
set {
replaceSubrange(strClosedRange(r), with: newValue)
}
}
/// `string[n...]`. See PartialRangeFrom
subscript(r: PartialRangeFrom<Int>) -> String {
get {
return String(self[strOpenRange(r.lowerBound..<self.count)])
}
set {
replaceSubrange(strOpenRange(r.lowerBound..<self.count), with: newValue)
}
}
/// `string[...n]`. See PartialRangeThrough
subscript(r: PartialRangeThrough<Int>) -> String {
get {
let upper = negativeIndex(r.upperBound)
return String(self[strClosedRange(0...upper, checkNegative: false)])
}
set {
let upper = negativeIndex(r.upperBound)
replaceSubrange(
strClosedRange(0...upper, checkNegative: false), with: newValue
)
}
}
/// `string[...<n]`. See PartialRangeUpTo
subscript(r: PartialRangeUpTo<Int>) -> String {
get {
let upper = negativeIndex(r.upperBound)
return String(self[strOpenRange(0..<upper, checkNegative: false)])
}
set {
let upper = negativeIndex(r.upperBound)
replaceSubrange(
strOpenRange(0..<upper, checkNegative: false), with: newValue
)
}
}
}
Usage:
let text = "012345"
print(text[2]) // "2"
print(text[-1] // "5"
print(text[1...3]) // "123"
print(text[2..<3]) // "2"
print(text[3...]) // "345"
print(text[...3]) // "0123"
print(text[..<3]) // "012"
print(text[(-3)...] // "345"
print(text[...(-2)] // "01234"
All of the above works with assignment as well. All subscripts have getters and setters.
a new extension added,
since String conforms to BidirectionalCollection Protocol
extension String{
subscript(at i: Int) -> String? {
get {
if i < count{
let idx = index(startIndex, offsetBy: i)
return String(self[idx])
}
else{
return nil
}
}
set {
if i < count{
let idx = index(startIndex, offsetBy: i)
remove(at: idx)
if let new = newValue, let first = new.first{
insert(first, at: idx)
}
}
}
}
}
call like this:
var str = "fighter"
str[at: 2] = "6"

Split string by two symbols in Swift

I would like to split the string by two symbols in Swift. So after string "df57g5df7g" I would like to obtain an Array ["df","57","g5","df","7g"].
Is it possible to force iterator
for i in word.characters {
print(i)
}
to jump by two symbols, and get acsess to the next symbol inside the loop?
A simple while loop:
let str = "df57g5df7g"
var startIndex = str.startIndex
var result = [String]()
repeat {
let endIndex = startIndex.advancedBy(2, limit: str.endIndex)
result.append(str[startIndex..<endIndex])
startIndex = endIndex
} while startIndex < str.endIndex
print(result)
Or something more Swifty:
let result = 0.stride(to: str.characters.count, by: 2).map { i -> String in
let startIndex = str.startIndex.advancedBy(i)
let endIndex = startIndex.advancedBy(2, limit: str.endIndex)
return str[startIndex..<endIndex]
}
This might not be the slickest solution, but it works:
var word = "df57g5df7g"
var pairsArray = [String]()
while word.characters.count > 1 {
let firstCharacter = word.removeAtIndex(word.startIndex)
let secondCharacter = word.removeAtIndex(word.startIndex)
pairsArray.append("\(firstCharacter)\(secondCharacter)")
}
print(pairsArray)
The result is:
["df", "57", "g5", "df", "7g"]
This is the best solution I've seen, taken from the SwiftSequence library.
extension CollectionType {
public func chunk(n: Index.Distance) -> [SubSequence] {
var res: [SubSequence] = []
var i = startIndex
var j: Index
while i != endIndex {
j = i.advancedBy(n, limit: endIndex)
res.append(self[i..<j])
i = j
}
return res
}
}
let word = "df57g5df7g"
let pairs = word.characters.chunk(2).map(String.init)
print(pairs) //["df", "57", "g5", "df", "7g"]
You can see it in action here.