How to swap two characters in String Swift4? - ios

in
Swift4, the String is Collections. You will no longer use
characters property on a string.
func swapCharacters(input:String,index1:Int,index2:Int)-> String { // logic to swap }
let input = "ABCDEFGH"
If I call the function with (input,3,8) then the output should be
Output : ABCHEFGD
Note: In Swift4, Strings are collections.

Fairly straightforward since String is a collection:
func swapCharacters(input: String, index1: Int, index2: Int) -> String {
var characters = Array(input)
characters.swapAt(index1, index2)
return String(characters)
}
let input = "ABCDEFGH"
print(swapCharacters(input: input, index1: 3, index2: 7)) //ABCHEFGD
or, to provide a direct array-like operation:
extension String {
mutating func swapAt(_ index1: Int, _ index2: Int) {
var characters = Array(self)
characters.swapAt(index1, index2)
self = String(characters)
}
}
var input = "ABCDEFGH"
input.swapAt(3, 7)
print(input) //ABCHEFGD

If you are looking for String manipulation without converting it to array.
I won't say it's great solution, but does its job.
func swapCharacters(input: String, index1: Int, index2: Int) -> String {
var result = input
let index1Start = input.index(input.startIndex, offsetBy: index1)
let index1End = input.index(after: index1Start)
let index2Start = input.index(input.startIndex, offsetBy: index2)
let index2End = input.index(after: index2Start)
let temp = input[index2Start..<index2End]
result.replaceSubrange(index2Start..<index2End,
with: input[index1Start..<index1End])
result.replaceSubrange(index1Start..<index1End, with: temp)
return result
}
print(swapCharacters(input: "ABCDEFGH", index1: 3, index2: 7))
prints: ABCHEFGD

Your question doesn't make sense. Strings in Swift are not indexed by Int. You need to first figure out for yourself what a Character really is (an Extended Grapheme Cluster). Then you need to figure out why Int cannot be used as the index for Strings. And then you can come up with a real problem. Read up here:
https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/StringsAndCharacters.html

Related

All possible combinations (subsets) in swift

I'm having this issue of trying to trying to recursively print out all subsets of the giving array of String(characters) using swift. The value is ("a1b2"). The output should have 4 subsets.
Currently stuck here:
func overall(string: String) {
helper(string: string, i: 0, slate: "")
}
func helper(string: String, i: Int, slate: String) {
var result = [Any]()
let word = Array(string)
var counter = i
if string.count == counter {
result.append(slate)
} else {
if word[i].isNumber {
counter += 1
helper(string: string, i: counter, slate: slate + String(word[i]))
} else if word[i].isLowercase {
counter += 1
helper(string: string, i: counter, slate: slate + String(word[i]).uppercased())
} else {
counter += 1
helper(string: string, i: counter, slate: slate + String(word[i]).lowercased())
}
}
}
overall(string: "a1b2")
I'm having issues creating a base case in the helper functions. Also I'm unsure if I'm using recursion properly. Could you please help with an explanation, it will be greatly appreciated.
I'm sure this is totally useless in general, but just for fun, here's an amusing recursion-free solution for the particular problem given, where we know the string is exactly four characters and we know that either uppercased or lowercased must be applied to each character:
let s = "a1b2"
let arr = Array(s).map(String.init)
var result : Set<String> = []
for i in 0b0000...0b1111 {
var tog = [Bool]()
for sh in 0...3 { tog.append(i & 1<<sh == 0) }
var word = ""
for ix in 0...3 {
let f = tog[ix] ? arr[ix].lowercased : arr[ix].uppercased
word = word + f()
}
result.insert(word)
}
print(result)
The OP clarified in comments that he wanted the case-variants of the original string, not "subsets" as originally stated
[Edit] I originally had a paragraph here about String.count, however, my memory must have been in error, because Apple's documentation does state that String.count is in fact the number of Characters, which is what we would all want it to be anyway. I hope my error didn't throw anyone too far off.
You don't need any counters. All you need is the first character, and recurse on the rest of the string.
The thing is, when you have a letter as your first you need to preprend both the upper and lower case variants to all of the strings returned by the recursive call.
The base case is at the end of the string, in which case you return an an array containing just the empty string.
Here's my implementation:
func caseVariants(of s: String) -> [String]
{
func caseVariants(of s: Substring) -> [String]
{
guard let c = s.first else { return [""] } // base case
let remainder = s[s.index(after: s.startIndex)...]
let remainderVariants = caseVariants(of: remainder)
var results: [String] = []
if c.isLetter
{
results.append(
contentsOf: remainderVariants.map {
"\(c.uppercased())" + $0
}
)
results.append(
contentsOf: remainderVariants.map {
"\(c.lowercased())" + $0
}
)
}
else
{
results.append(
contentsOf: remainderVariants.map { "\(c)" + $0 }
)
}
return results
}
return caseVariants(of: s[...]).sorted()
}
print("Case variants:")
for s in caseVariants(of: "a1b2") { print("\"\(s)\"") }
The output is:
Case variants:
"A1B2"
"A1b2"
"a1B2"
"a1b2"
[EDIT] in comments, OP asked what if .startIndex were disallowed (such as in an interview). While I think such a restriction is insane, there is an easy solution, and it's a one-line, quite reasonable change to my previous code. Change this line:
let remainder = s[s.index(after: s.startIndex)...]
to use .dropFirst()
let remainder = s.dropFirst()
If we look at the implementation of dropFirst in the Collection protocol in the standard library:
#inlinable
public __consuming func dropFirst(_ k: Int = 1) -> SubSequence {
_precondition(k >= 0, "Can't drop a negative number of elements from a collection")
let start = index(startIndex, offsetBy: k, limitedBy: endIndex) ?? endIndex
return self[start..<endIndex]
}
We see that the use of dropFirst will use the default value of 1 for k. In that case, when we've already checked that we're not at the end of the string, the line
let start = index(startIndex, offsetBy: k, limitedBy: endIndex) ?? endIndex
is equivalent to
let start = index(after: startIndex)
which means that the returned substring is
return self[index(after: startIndex)..<endIndex]
which is just the canonical way of saying:
return self[index(after: startIndex)...]
So a version using dropFirst() is identical to the original solution once inlining has done its thing.

What is the equivalent of the Visual Basic "Mid" function in Swift 4?

With the new ways of handling string in Swift 4, I'm trying to wrap my head around how to write the equivalent of the Mid function from other languages (visual basic, etc), so that
let testString = "0123456"
print Mid(testString, 2,4) // "1234" (or "2345" would work too)
This question is the same idea, but everything there predates Swift 4. If the answer there by jlert is still the best way to do things in Swift 4, that works, although it seems like so much has changed that the best practice to do this may have changed as well.
One way to do it is with a combination of dropFirst and prefix and then use String to convert the result back to String:
let testString = "0123456"
func mid(_ str: String, _ low: Int, _ count: Int) -> String {
return String(str.dropFirst(low).prefix(count))
}
print(mid(testString, 2,4)) // 2345
dropFirst and prefix are forgiving and won't crash if enough letters are not there. They will just give a truncated result. Depending on how you define the function, this is a perfectly acceptable implementation.
Another approach would be to use array subscripting. If you do it that way, you need to check inputs to avoid Array index out of range fatal errors:
func mid(_ str: String, _ low: Int, _ count: Int) -> String? {
guard low >= 0,
count >= 0,
low < str.count,
low + count <= str.count
else { return nil }
return String(Array(str)[low ..< low + count])
}
let testString = "abcdefghi"
if let result = mid(testString, 2, 4) {
print(result) // cdef
}
You can do:
extension String {
func mid(_ startOffset: Int, _ length: Int) -> Substring {
let start = index(startIndex, offsetBy: startOffset, limitedBy: endIndex) ?? endIndex
let end = index(start, offsetBy: length, limitedBy: endIndex) ?? endIndex
return self[start ..< end]
}
}
let string = "0123456"
let result = string.mid(2, 4) // 2345
Note, this returns a Substring, which enjoys memory efficiency. But if you want a String, you can do :
let result = String(string.mid(2, 4))
(Or you can incorporate this in your mid function.)
I would take a different approach using String.Index and return a Substring instead of a new String object. You can also add precondition to restrict improper use of that method:
func mid(_ string: String, _ positon: Int, length: Int) -> Substring {
precondition(positon < string.count, "invalid position")
precondition(positon + length <= string.count, "invalid substring length")
let lower = string.index(string.startIndex, offsetBy: positon)
let upper = string.index(lower, offsetBy: length)
return string[lower..<upper]
}
let testString = "0123456"
mid(testString, 2, length: 4) // "2345"
Another option would be creating that method as a string extension:
extension String {
func mid(_ positon: Int, length: Int) -> Substring {
precondition(positon < count, "invalid position")
precondition(positon + length <= count, "invalid substring length")
let lower = index(startIndex, offsetBy: positon)
let upper = index(lower, offsetBy: length)
return self[lower..<upper]
}
}
let testString = "0123456"
testString.mid(2, length: 4) // "2345"
iOS 14, Swift 5... same thing as vacawama excellent answer, but as an extension, so it's even less code :)
extension String {
func mid(_ low: Int, _ count: Int) -> String {
return String(self.dropFirst(low).prefix(count))
}
}
Use it like this.
let pString = "01234567"
for i in 0 ..< 8 {
print("\(i) \(pString.mid(i,1)")
}
Prints out your string to the console, one character at a time

how to use "0" to fill the rest space of the first string when I copy scond string into the first string in swift?

For example, I have a string: let one = "1" , and I have another string:
let two = "123"
Now I have a function:
func myfunc(head:String,body:String) -> String
{
//following are pseudo code
var return_string: String
return_string[0...2] = head
// the passing parameter: head's length will be equal or less than 3 charactors
//if the string: head's length is less than 3, the other characters will be filled via "0"
return_string[3...end] = body//the rest of return_string will be the second passing parameter: body
}
For example, if I call this function like this:
myfunc(one,"hello")
it will return 001hello, if I call it like this:
myfunc(two,"hello")
it will return 123hello
if I call it like this:
myfunc("56","wolrd")
it will return
056world
Here is my extension to String:
extension String {
var length: Int {
return self.characters.count
}
subscript (i:Int) -> Character{
return self[self.startIndex.advancedBy(i)]
}
subscript (i: Int) -> String {
return String(self[i] as Character)
}
subscript (r: Range<Int>) -> String {
return substringWithRange(Range(start: startIndex.advancedBy(r.startIndex), end: startIndex.advancedBy(r.endIndex)))
}
}
what should I do ? How to insert 0 at the beginning of the string:return_value?
There are many possible approaches, this is just one:
func myfunc(head:String, _ body:String) -> String
{
return String(("000" + head).characters.suffix(3)) + body
}
It works by taking the last three characters of "000" + head:
myfunc("1", "hello") // "001hello"
myfunc("123", "hello") // "123hello"
myfunc("56", "world") // "056world"
First off, you don't need the String extension. This method will append 0's to the beginning of head until it is 3 characters, then concatenate the two strings.
func myFunc(var head: String, body: String) -> String {
while head.characters.count < 3 {
head.insert("0", atIndex: head.startIndex)
}
head = head.substringToIndex(head.startIndex.advancedBy(3))
return head + body
}

How to write generic function with array input of floating point convertibles in Swift?

I am taking my first foray into writing generic functions in Swift. What I am trying to do is write a function that takes an array input of any type as long as that type is convertible to a floating point number. I am wondering if I can leverage some of the Swift standard library protocols to do this. Here is a trivial example (I am searching for what to use as ConvertibleToFloatingPointTypeProtocol):
func toDoubleArray<T: ConvertibleToFloatingPointTypeProtocol>(array: [T]) -> [Double] {
var doubleArray = [Double]()
for arrayItem in array {
doubleArray.append(Double(arrayItem))
}
return doubleArray
}
The compiler error I get from this when I try FloatingPointType, etc. is: "Cannot find an initializer for type 'Double' that accepts an argument list of type '(T)'"
Now I know another option is to create my own protocol and then extend the types that I am interested in to adopt it, but this just feels like something that exists right under my nose.
Try FloatLiteralConvertible:
import Darwin
// Swift 2.0
func toDoubleArray<T : FloatLiteralConvertible>(arr : [T]) -> [Double] {
return arr.flatMap { $0 as? Double }
}
// Swift 1.2
func toDoubleArray<T : FloatLiteralConvertible>(arr : [T]) -> [Double] {
var result = [Double]()
for a in arr {
if let d = a as? Double {
result.append(d)
}
}
return result
}
let a = toDoubleArray([1, 2, 3])
let b = toDoubleArray([M_PI, 2 as Int, 3.3])
let c = toDoubleArray(["a", "b", "c"]) // Error, cannot convert [String] to [Double]
let d = toDoubleArray([1, 2, 3, "a"]) // Error, cannot convert [NSObject] to [Double]

what does this line of code in the for statement mean or do?

am studying with a tutorial for a game app and there is a line of code that i didn't understood it looks like it's of type tuple
this is my code:
var algorithmResult = algorithm(value: value)
func rowCheck(#value: Int) -> (location: String, pattern: String)? {
var acceptableFinds = ["011", "101", "110"]
var findFunc = [checkTop, checkBottom, checkMiddleAcross, checkRight, checkMiddleDown, checkLeft, checkDiagLeftRight, checkDiagRightLeft]
for algorithm in findFunc {
var algorithmResult = algorithm(value: value)
if (find(acceptableFinds, algorithmResult.pattern) != nil) {
return algorithmResult
}
}
return nil
}
In:
var algorithmResult = algorithm(value: value)
algorithm represents one element in the findFunc array (as defined in for algorithm in findFunc).
From the names, I'm guessing each of those elements is a function. Those functions are passed value and the result of the function is stored in algorithmResult.
Here's a similar example. Create two functions:
func add(operand : Int) -> Int {
return operand + operand
}
func multiply(operand : Int) -> Int {
return operand * operand
}
Store them in an array:
let funcs = [add, multiply]
Call them in a loop:
for function in funcs {
let x = function(5)
print(x)
}
This prints:
10
25
It applies each function from findFunc array to the value, which was passed to rowCheck function.

Resources