I am new to swift and I am trying to count the different characters in a string but my code returns the value for the whole String
for example:
var string aString = "aabb"
aString.characters.count() //returns 5
counter = 0
let a = "a"
for a in aString.characters {
counter++
} //equally returns 5
Can somebody explain why this is happening and how I could count the different chars?
It looks there is some confusion about what you really need.
I tried to answer to the 5 most likely interpretations.
var word = "aabb"
let numberOfChars = word.characters.count // 4
let numberOfDistinctChars = Set(word.characters).count // 2
let occurrenciesOfA = word.characters.filter { $0 == "A" }.count // 0
let occurrenciesOfa = word.characters.filter { $0 == "a" }.count // 2
let occurrenciesOfACaseInsensitive = word.characters.filter { $0 == "A" || $0 == "a" }.count // 2
print(occurrenciesOfA)
print(occurrenciesOfa)
print(occurrenciesOfACaseInsensitive)
check this
var aString = "aabb"
aString.characters.count // 4
var counter = 0
let a = "a" // you newer use this in your code
for thisIsSingleCharacterInStringCharactersView in aString.characters {
counter++
}
print(counter) // 4
it simply increase your counter for each character
to calculate number of different characters in you string, you probably can use something 'more advanced', like in next example
let str = "aabbcsdfaewdsrsfdeewraewd"
let dict = str.characters.reduce([:]) { (d, c) -> Dictionary<Character,Int> in
var d = d
let i = d[c] ?? 0
d[c] = i+1
return d
}
print(dict) // ["b": 2, "a": 4, "w": 3, "r": 2, "c": 1, "s": 3, "f": 2, "e": 4, "d": 4]
You code is quite faulty: it should probably start with
let aString = "aabb"
The solutions is to get the characters, put them into a set (unique) and then counting the members of the set:
let differentChars = Set(aString.characters).count
Correctly returns
2
The characters property is deprecated, you can use components(separatedBy:) to find how many characters in a String. eg,
extension String {
public func numberOfOccurrences(_ string: String) -> Int {
return components(separatedBy: string).count - 1
}
}
let aString = "aabbaa"
let aCount = aString.numberOfOccurrences("a") // aCount = 4
Updated #Luca Angeletti's answer for Swift5.3 because characters property is unavailable in newer swift version.
var word = "aabb"
let numberOfChars = word.count // 4
let numberOfDistinctChars = Set(word).count // 2
let occurrenciesOfA = word.filter { $0 == "A" }.count // 0
let occurrenciesOfa = word.filter { $0 == "a" }.count // 2
let occurrenciesOfACaseInsensitive = word.filter { $0 == "A" || $0 == "a" }.count // 2
print(numberOfChars)
print(numberOfDistinctChars)
print(occurrenciesOfA)
print(occurrenciesOfa)
print(occurrenciesOfACaseInsensitive)
Construct a dictionary out of a sequence of (key, value) pairs. If we can guarantee that the keys are unique, we can use Dictionary(uniqueKeysWithValues:).
func characterFrequencies(of string: String) -> Dictionary<String.Element, Int> {
let frequencyPair = string.map { ($0, 1) }
return Dictionary(frequencyPair, uniquingKeysWith: +)
}
Usage : print(characterFrequencies(of: "Happy"))
Result : ["a": 1, "H": 1, "y": 1, "p": 2]
func repeatedCharaterPrint(inputArray: [String]) -> [String:Int] {
var dict = [String:Int]()
if inputArray.count > 0 {
for char in inputArray {
if let keyExists = dict[char], keyExists != nil {
dict[char] = Int(dict[char] ?? 0) + 1
}else {
dict[char] = 1
}
}
}
return dict
}
let aa = ["a","s","f","s","l","s"]
print(repeatedCharaterPrint(inputArray: aa))
//Answer : "["s": 3, "l": 1, "a": 1, "f": 1]"
This solution is written using hash function , so computation time would be O(1). good for long strings.
//Extension On String and Characters to get Ascii values and Char from Ascii
extension Character {
//Get Ascii Value of Char
var asciiValue:UInt32? {
return String(self).unicodeScalars.filter{$0.isASCII}.first?.value
}
}
extension String {
//Char Char from Ascii Value
init(unicodeScalar: UnicodeScalar) {
self.init(Character(unicodeScalar))
}
init?(unicodeCodepoint: Int) {
if let unicodeScalar = UnicodeScalar(unicodeCodepoint) {
self.init(unicodeScalar: unicodeScalar)
} else {
return nil
}
}
static func +(lhs: String, rhs: Int) -> String {
return lhs + String(unicodeCodepoint: rhs)!
}
static func +=(lhs: inout String, rhs: Int) {
lhs = lhs + rhs
}
}
extension String {
///Get Char at Index from String
var length: Int {
return self.characters.count
}
subscript (i: Int) -> String {
return self[Range(i ..< i + 1)]
}
func substring(from: Int) -> String {
return self[Range(min(from, length) ..< length)]
}
func substring(to: Int) -> String {
return self[Range(0 ..< max(0, to))]
}
subscript (r: Range<Int>) -> String {
let range = Range(uncheckedBounds: (lower: max(0, min(length, r.lowerBound)),
upper: min(length, max(0, r.upperBound))))
let start = index(startIndex, offsetBy: range.lowerBound)
let end = index(start, offsetBy: range.upperBound - range.lowerBound)
return self[Range(start ..< end)]
}
}
//Program :
let strk = "aacncjkvkevkklvkdsjkbvjsdbvjkbsdjkvbjdsbvjkbsvbkjwlnkneilhfleknkeiohlgblehgilkbskdbvjdsbvjkdsbvbbvsbdvjlbsdvjbvjkdbvbsjdbjsbvjbdjbjbjkbjkvbjkbdvjbdjkvbjdbvjdbvjbvjdsbjkvbdsjvbkjsbvadvbjkenevknkenvnekvjksbdjvbjkbjbvbkjvbjdsbvjkbdskjvbdsbvjkdsbkvbsdkjbvkjsbvjsbdjkvbdsbvjkbdsvjbdefghaj"
print(strk)
//Declare array of fixes size 26 (characters) or you can say it as a hash table
var freq = [Int](repeatElement(0, count: 26))
func hashFunc(char : Character) -> UInt32 {
guard let ascii = char.asciiValue else {
return 0
}
return ascii - 97 //97 used for ascii value of a
}
func countFre(string:String) {
for i in 0 ... string.characters.count-1 {
let charAtIndex = string[i].characters.first!
let index = hashFunc(char: charAtIndex)
let currentVal = freq[Int(index)]
freq[Int(index)] = currentVal + 1
//print("CurrentVal of \(charAtIndex) with index \(index) is \(currentVal)")
}
for charIndex in 0 ..< 26 {
print(String(unicodeCodepoint: charIndex+97)!,freq[charIndex])
}
}
countFre(string: strk)
Related
How to decode String from percent encoded windowsCP1251?
replacingPercentEscapes(using: String.Encoding.windowsCP1251) now is obsolete
removingPercentEncoding uses utf8
I have found the solution. It works for me. Welcome to refactor my example.
extension String {
func removingPercentEncoding(using encoding: String.Encoding) -> String {
let firstChar = self.first
let percentCharacter = Character("%")
var encodedPrefix: String.SubSequence? = nil
var encodedSuffix = self
if firstChar != percentCharacter {
if let indexOfFirstPercentChar = index(of: percentCharacter) {
encodedPrefix = self[..<indexOfFirstPercentChar]
encodedSuffix = String(self[indexOfFirstPercentChar...])
} else {
//no % char at all. Nothing encoded
return self
}
}
let substrings = encodedSuffix.components(separatedBy: "%")
let arr = substrings.map{ substring -> (String) in
switch substring.count {
case let count where count < 2:
return substring
case let count where count == 2:
let decodedArr = substring.hexa2Bytes
let data = Data(decodedArr)
if let decodedStr = String(data: data, encoding: encoding) {
return decodedStr
}
return substring
default: //>2
let thirdSymbolIndex = index(startIndex, offsetBy: 2)
let firstTwo = substring[..<thirdSymbolIndex]
let furhter = substring[thirdSymbolIndex...]
let decodedArr = String(firstTwo).hexa2Bytes
let data = Data(decodedArr)
if let decodedStr = String(data: data, encoding: encoding) {
return decodedStr + furhter
}
return substring
}
}
let result = arr.joined()
return String(encodedPrefix ?? "") + result
}
var hexa2Bytes: [UInt8] {
let hexa = Array(characters)
return stride(from: 0, to: characters.count, by: 2).flatMap { UInt8(String(hexa[$0..<$0.advanced(by: 2)]), radix: 16) }
}
}
A little example which is expected to work with multi-byte string encodings.
extension UInt8 {
//returns 0...15 when '0'...'9', 'A'...'F', 'a'...'f', otherwise returns nil
var hexValue: UInt8? {
if UInt8(ascii: "0") <= self && self <= UInt8(ascii: "9") {
return self - UInt8(ascii: "0")
} else if UInt8(ascii: "A") <= self && self <= UInt8(ascii: "F") {
return self - UInt8(ascii: "A") + 10
} else if UInt8(ascii: "a") <= self && self <= UInt8(ascii: "f") {
return self - UInt8(ascii: "a") + 10
} else {
return nil
}
}
}
extension String {
func removingPercentEncoding(using encoding: String.Encoding) -> String? {
guard let percentEncodedData = self.data(using: encoding) else {return nil}
var byteIterator = percentEncodedData.makeIterator()
var percentDecodedData = Data()
while let b0 = byteIterator.next() {
guard b0 == UInt8(ascii: "%"), let b1 = byteIterator.next() else {
//Non percent character
percentDecodedData.append(b0)
continue
}
guard let h1 = b1.hexValue, let b2 = byteIterator.next() else {
//Keep it as is, when invalid hex-sequece appeared
percentDecodedData.append(b0)
percentDecodedData.append(b1)
continue
}
guard let h2 = b2.hexValue else {
//Keep it as is, when invalid hex-sequece appeared
percentDecodedData.append(b0)
percentDecodedData.append(b1)
percentDecodedData.append(b2)
continue
}
percentDecodedData.append((h1<<4) + h2)
}
return String(data: percentDecodedData, encoding: encoding)
}
}
In my opinion, you should think non-UTF8 percent encoding now is obsolete, and should fix the part which is generating the percent encoded string with CP1251.
I have a string which contains binary digits. How to separate it in to pairs of digits?
Suppose the string is:
let x = "11231245"
I want to add a separator such as ":" (i.e., a colon) after each 2 characters.
I would like the output to be:
"11:23:12:45"
How could I do this in Swift ?
Swift 5.2 β’ Xcode 11.4 or later
extension Collection {
func unfoldSubSequences(limitedTo maxLength: Int) -> UnfoldSequence<SubSequence,Index> {
sequence(state: startIndex) { start in
guard start < endIndex else { return nil }
let end = index(start, offsetBy: maxLength, limitedBy: endIndex) ?? endIndex
defer { start = end }
return self[start..<end]
}
}
func every(n: Int) -> UnfoldSequence<Element,Index> {
sequence(state: startIndex) { index in
guard index < endIndex else { return nil }
defer { let _ = formIndex(&index, offsetBy: n, limitedBy: endIndex) }
return self[index]
}
}
var pairs: [SubSequence] { .init(unfoldSubSequences(limitedTo: 2)) }
}
extension StringProtocol where Self: RangeReplaceableCollection {
mutating func insert<S: StringProtocol>(separator: S, every n: Int) {
for index in indices.every(n: n).dropFirst().reversed() {
insert(contentsOf: separator, at: index)
}
}
func inserting<S: StringProtocol>(separator: S, every n: Int) -> Self {
.init(unfoldSubSequences(limitedTo: n).joined(separator: separator))
}
}
Testing
let str = "112312451"
let final0 = str.unfoldSubSequences(limitedTo: 2).joined(separator: ":")
print(final0) // "11:23:12:45:1"
let final1 = str.pairs.joined(separator: ":")
print(final1) // "11:23:12:45:1"
let final2 = str.inserting(separator: ":", every: 2)
print(final2) // "11:23:12:45:1\n"
var str2 = "112312451"
str2.insert(separator: ":", every: 2)
print(str2) // "11:23:12:45:1\n"
var str3 = "112312451"
str3.insert(separator: ":", every: 3)
print(str3) // "112:312:451\n"
var str4 = "112312451"
str4.insert(separator: ":", every: 4)
print(str4) // "1123:1245:1\n"
I'll go for this compact solution (in Swift 4) :
let s = "11231245"
let r = String(s.enumerated().map { $0 > 0 && $0 % 2 == 0 ? [":", $1] : [$1]}.joined())
You can make an extension and parameterize the stride and the separator so that you can use it for every value you want (In my case, I use it to dump 32-bit space-operated hexadecimal data):
extension String {
func separate(every stride: Int = 4, with separator: Character = " ") -> String {
return String(enumerated().map { $0 > 0 && $0 % stride == 0 ? [separator, $1] : [$1]}.joined())
}
}
In your case this gives the following results:
let x = "11231245"
print (x.separate(every:2, with: ":")
$ 11:23:12:45
Swift 5.3
/// Adds a separator at every N characters
/// - Parameters:
/// - separator: the String value to be inserted, to separate the groups. Default is " " - one space.
/// - stride: the number of characters in the group, before a separator is inserted. Default is 4.
/// - Returns: Returns a String which includes a `separator` String at every `stride` number of characters.
func separated(by separator: String = " ", stride: Int = 4) -> String {
return enumerated().map { $0.isMultiple(of: stride) && ($0 != 0) ? "\(separator)\($1)" : String($1) }.joined()
}
Short and simple, add a let or two if you want
extension String {
func separate(every: Int, with separator: String) -> String {
return String(stride(from: 0, to: Array(self).count, by: every).map {
Array(Array(self)[$0..<min($0 + every, Array(self).count)])
}.joined(separator: separator))
}
}
let a = "separatemepleaseandthankyou".separate(every: 4, with: " ")
a is
sepa rate mepl ease andt hank you
Its my code in swift 4
let x = "11231245"
var newText = String()
for (index, character) in x.enumerated() {
if index != 0 && index % 2 == 0 {
newText.append(":")
}
newText.append(String(character))
}
print(newText)
Outputs 11:23:12:45
My attempt at that code would be:
func insert(seperator: String, afterEveryXChars: Int, intoString: String) -> String {
var output = ""
intoString.characters.enumerate().forEach { index, c in
if index % afterEveryXChars == 0 && index > 0 {
output += seperator
}
output.append(c)
}
return output
}
insert(":", afterEveryXChars: 2, intoString: "11231245")
Which outputs
11:23:12:45
let y = String(
x.characters.enumerate().map() {
$0.index % 2 == 0 ? [$0.element] : [$0.element, ":"]
}.flatten()
)
A simple One line of code for inserting separater ( Swift 4.2 ):-
let testString = "123456789"
let ansTest = testString.enumerated().compactMap({ ($0 > 0) && ($0 % 2 == 0) ? ":\($1)" : "\($1)" }).joined() ?? ""
print(ansTest) // 12:34:56:78:9
Swift 4.2.1 - Xcode 10.1
extension String {
func insertSeparator(_ separatorString: String, atEvery n: Int) -> String {
guard 0 < n else { return self }
return self.enumerated().map({String($0.element) + (($0.offset != self.count - 1 && $0.offset % n == n - 1) ? "\(separatorString)" : "")}).joined()
}
mutating func insertedSeparator(_ separatorString: String, atEvery n: Int) {
self = insertSeparator(separatorString, atEvery: n)
}
}
Usage
let testString = "11231245"
let test1 = testString.insertSeparator(":", atEvery: 2)
print(test1) // 11:23:12:45
var test2 = testString
test2.insertedSeparator(",", atEvery: 3)
print(test2) // 112,312,45
I'm little late here, but i like to use regex like in this:
extension String {
func separating(every: Int, separator: String) -> String {
let regex = #"(.{\#(every)})(?=.)"#
return self.replacingOccurrences(of: regex, with: "$1\(separator)", options: [.regularExpression])
}
}
"111222333".separating(every: 3, separator: " ")
the output:
"111 222 333"
extension String{
func separate(every: Int) -> [String] {
return stride(from: 0, to: count, by: every).map {
let ix0 = index(startIndex, offsetBy: $0);
let ix1 = index(after:ix0);
if ix1 < endIndex {
return String(self[ix0...ix1]);
}else{
return String(self[ix0..<endIndex]);
}
}
}
/// or O(1) implementation (without count)
func separate(every: Int) -> [String] {
var parts:[String] = [];
var ix1 = startIndex;
while ix1 < endIndex {
let ix0 = ix1;
var n = 0;
while ix1 < endIndex && n < every {
ix1 = index(after: ix1);
n += 1;
}
parts.append(String(self[ix0..<ix1]));
}
return parts;
}
"asdf234sdf".separate(every: 2).joined(separator: ":");
A simple String extension that doesn't require the original string to be a multiple of the step size (increment):
extension String {
func inserted(_ newElement: Character,atEach increment:Int)->String {
var newStr = self
for indx in stride(from: increment, to: newStr.count, by: increment).reversed() {
let index = String.Index(encodedOffset: indx)
newStr.insert(newElement, at: index)
}
return newStr
}
}
I want to modify a Swift string by converting some characters to uppercase, some others to lowercase.
In Obj-c I had following :
- (NSString*) lowercaseDestination:(NSString*) string {
NSUInteger length = string.length;
unichar buf[length+1];
[string getCharacters:buf];
BOOL up = true;
for (int i=0; i< length ; i++) {
unichar chr = buf[i];
if( .... ) {
buf[i] = toupper(chr);
} else {
buf[i] = tolower(chr);
}
}
string = [NSString stringWithCharacters:buf length:length];
return string;
How would you do that in Swift 2 ?
I did no find any Character method to upper or lower the case.
Would be an array of String of 1 character be an option ? (And then use String methods to upper and lower each String
String has a upperCaseString method, but Character doesn't.
The reason is that in exotic languages like German, converting a single
character to upper case can result in multiple characters:
print("Γ".uppercaseString) // "SS"
The toupper/tolower functions are not Unicode-safe and not
available in Swift.
So you can enumerate the string characters, convert each character to
a string, convert that to upper/lowercase, and concatenate the results:
func lowercaseDestination(str : String) -> String {
var result = ""
for c in str.characters {
let s = String(c)
if condition {
result += s.lowercaseString
} else {
result += s.uppercaseString
}
}
return result
}
which can be written more compactly as
func lowercaseDestination(str : String) -> String {
return "".join(str.characters.map { c -> String in
let s = String(c)
return condition ? s.lowercaseString : s.uppercaseString
})
}
Re your comment: If the condition needs to check more than one
character then you might want to create an array of all characters
first:
func lowercaseDestination(str : String) -> String {
var result = ""
let characters = Array(str.characters)
for i in 0 ..< characters.count {
let s = String(characters[i])
if condition {
result += s.lowercaseString
} else {
result += s.uppercaseString
}
}
return result
}
It's time to admit defeat...
In Objective-C, I could use something like:
NSString* str = #"abcdefghi";
[str rangeOfString:#"c"].location; // 2
In Swift, I see something similar:
var str = "abcdefghi"
str.rangeOfString("c").startIndex
...but that just gives me a String.Index, which I can use to subscript back into the original string, but not extract a location from.
FWIW, that String.Index has a private ivar called _position that has the correct value in it. I just don't see how it's exposed.
I know I could easily add this to String myself. I'm more curious about what I'm missing in this new API.
You are not the only one who couldn't find the solution.
String doesn't implement RandomAccessIndexType. Probably because they enable characters with different byte lengths. That's why we have to use string.characters.count (count or countElements in Swift 1.x) to get the number of characters. That also applies to positions. The _position is probably an index into the raw array of bytes and they don't want to expose that. The String.Index is meant to protect us from accessing bytes in the middle of characters.
That means that any index you get must be created from String.startIndex or String.endIndex (String.Index implements BidirectionalIndexType). Any other indices can be created using successor or predecessor methods.
Now to help us with indices, there is a set of methods (functions in Swift 1.x):
Swift 4.x
let text = "abc"
let index2 = text.index(text.startIndex, offsetBy: 2) //will call succ 2 times
let lastChar: Character = text[index2] //now we can index!
let characterIndex2 = text.index(text.startIndex, offsetBy: 2)
let lastChar2 = text[characterIndex2] //will do the same as above
let range: Range<String.Index> = text.range(of: "b")!
let index: Int = text.distance(from: text.startIndex, to: range.lowerBound)
Swift 3.0
let text = "abc"
let index2 = text.index(text.startIndex, offsetBy: 2) //will call succ 2 times
let lastChar: Character = text[index2] //now we can index!
let characterIndex2 = text.characters.index(text.characters.startIndex, offsetBy: 2)
let lastChar2 = text.characters[characterIndex2] //will do the same as above
let range: Range<String.Index> = text.range(of: "b")!
let index: Int = text.distance(from: text.startIndex, to: range.lowerBound)
Swift 2.x
let text = "abc"
let index2 = text.startIndex.advancedBy(2) //will call succ 2 times
let lastChar: Character = text[index2] //now we can index!
let lastChar2 = text.characters[index2] //will do the same as above
let range: Range<String.Index> = text.rangeOfString("b")!
let index: Int = text.startIndex.distanceTo(range.startIndex) //will call successor/predecessor several times until the indices match
Swift 1.x
let text = "abc"
let index2 = advance(text.startIndex, 2) //will call succ 2 times
let lastChar: Character = text[index2] //now we can index!
let range = text.rangeOfString("b")
let index: Int = distance(text.startIndex, range.startIndex) //will call succ/pred several times
Working with String.Index is cumbersome but using a wrapper to index by integers (see https://stackoverflow.com/a/25152652/669586) is dangerous because it hides the inefficiency of real indexing.
Note that Swift indexing implementation has the problem that indices/ranges created for one string cannot be reliably used for a different string, for example:
Swift 2.x
let text: String = "abc"
let text2: String = "πΎππ"
let range = text.rangeOfString("b")!
//can randomly return a bad substring or throw an exception
let substring: String = text2[range]
//the correct solution
let intIndex: Int = text.startIndex.distanceTo(range.startIndex)
let startIndex2 = text2.startIndex.advancedBy(intIndex)
let range2 = startIndex2...startIndex2
let substring: String = text2[range2]
Swift 1.x
let text: String = "abc"
let text2: String = "πΎππ"
let range = text.rangeOfString("b")
//can randomly return nil or a bad substring
let substring: String = text2[range]
//the correct solution
let intIndex: Int = distance(text.startIndex, range.startIndex)
let startIndex2 = advance(text2.startIndex, intIndex)
let range2 = startIndex2...startIndex2
let substring: String = text2[range2]
Swift 3.0 makes this a bit more verbose:
let string = "Hello.World"
let needle: Character = "."
if let idx = string.characters.index(of: needle) {
let pos = string.characters.distance(from: string.startIndex, to: idx)
print("Found \(needle) at position \(pos)")
}
else {
print("Not found")
}
Extension:
extension String {
public func index(of char: Character) -> Int? {
if let idx = characters.index(of: char) {
return characters.distance(from: startIndex, to: idx)
}
return nil
}
}
In Swift 2.0 this has become easier:
let string = "Hello.World"
let needle: Character = "."
if let idx = string.characters.indexOf(needle) {
let pos = string.startIndex.distanceTo(idx)
print("Found \(needle) at position \(pos)")
}
else {
print("Not found")
}
Extension:
extension String {
public func indexOfCharacter(char: Character) -> Int? {
if let idx = self.characters.indexOf(char) {
return self.startIndex.distanceTo(idx)
}
return nil
}
}
Swift 1.x implementation:
For a pure Swift solution one can use:
let string = "Hello.World"
let needle: Character = "."
if let idx = find(string, needle) {
let pos = distance(string.startIndex, idx)
println("Found \(needle) at position \(pos)")
}
else {
println("Not found")
}
As an extension to String:
extension String {
public func indexOfCharacter(char: Character) -> Int? {
if let idx = find(self, char) {
return distance(self.startIndex, idx)
}
return nil
}
}
Swift 5.0
public extension String {
func indexInt(of char: Character) -> Int? {
return firstIndex(of: char)?.utf16Offset(in: self)
}
}
Swift 4.0
public extension String {
func indexInt(of char: Character) -> Int? {
return index(of: char)?.encodedOffset
}
}
extension String {
// MARK: - sub String
func substringToIndex(index:Int) -> String {
return self.substringToIndex(advance(self.startIndex, index))
}
func substringFromIndex(index:Int) -> String {
return self.substringFromIndex(advance(self.startIndex, index))
}
func substringWithRange(range:Range<Int>) -> String {
let start = advance(self.startIndex, range.startIndex)
let end = advance(self.startIndex, range.endIndex)
return self.substringWithRange(start..<end)
}
subscript(index:Int) -> Character{
return self[advance(self.startIndex, index)]
}
subscript(range:Range<Int>) -> String {
let start = advance(self.startIndex, range.startIndex)
let end = advance(self.startIndex, range.endIndex)
return self[start..<end]
}
// MARK: - replace
func replaceCharactersInRange(range:Range<Int>, withString: String!) -> String {
var result:NSMutableString = NSMutableString(string: self)
result.replaceCharactersInRange(NSRange(range), withString: withString)
return result
}
}
I have found this solution for swift2:
var str = "abcdefghi"
let indexForCharacterInString = str.characters.indexOf("c") //returns 2
I'm not sure how to extract the position from String.Index, but if you're willing to fall back on some Objective-C frameworks, you can bridge to objective-c and do it the same way you used to.
"abcdefghi".bridgeToObjectiveC().rangeOfString("c").location
It seems like some NSString methods haven't yet been (or maybe won't be) ported to String. Contains also comes to mind.
Here is a clean String extention that answers the question:
Swift 3:
extension String {
var length:Int {
return self.characters.count
}
func indexOf(target: String) -> Int? {
let range = (self as NSString).range(of: target)
guard range.toRange() != nil else {
return nil
}
return range.location
}
func lastIndexOf(target: String) -> Int? {
let range = (self as NSString).range(of: target, options: NSString.CompareOptions.backwards)
guard range.toRange() != nil else {
return nil
}
return self.length - range.location - 1
}
func contains(s: String) -> Bool {
return (self.range(of: s) != nil) ? true : false
}
}
Swift 2.2:
extension String {
var length:Int {
return self.characters.count
}
func indexOf(target: String) -> Int? {
let range = (self as NSString).rangeOfString(target)
guard range.toRange() != nil else {
return nil
}
return range.location
}
func lastIndexOf(target: String) -> Int? {
let range = (self as NSString).rangeOfString(target, options: NSStringCompareOptions.BackwardsSearch)
guard range.toRange() != nil else {
return nil
}
return self.length - range.location - 1
}
func contains(s: String) -> Bool {
return (self.rangeOfString(s) != nil) ? true : false
}
}
You can also find indexes of a character in a single string like this,
extension String {
func indexes(of character: String) -> [Int] {
precondition(character.count == 1, "Must be single character")
return self.enumerated().reduce([]) { partial, element in
if String(element.element) == character {
return partial + [element.offset]
}
return partial
}
}
}
Which gives the result in [String.Distance] ie. [Int], like
"apple".indexes(of: "p") // [1, 2]
"element".indexes(of: "e") // [0, 2, 4]
"swift".indexes(of: "j") // []
Swift 5
Find index of substring
let str = "abcdecd"
if let range: Range<String.Index> = str.range(of: "cd") {
let index: Int = str.distance(from: str.startIndex, to: range.lowerBound)
print("index: ", index) //index: 2
}
else {
print("substring not found")
}
Find index of Character
let str = "abcdecd"
if let firstIndex = str.firstIndex(of: "c") {
let index: Int = str.distance(from: str.startIndex, to: firstIndex)
print("index: ", index) //index: 2
}
else {
print("symbol not found")
}
If you want to use familiar NSString, you can declare it explicitly:
var someString: NSString = "abcdefghi"
var someRange: NSRange = someString.rangeOfString("c")
I'm not sure yet how to do this in Swift.
If you want to know the position of a character in a string as an int value use this:
let loc = newString.range(of: ".").location
This worked for me,
var loc = "abcdefghi".rangeOfString("c").location
NSLog("%d", loc);
this worked too,
var myRange: NSRange = "abcdefghi".rangeOfString("c")
var loc = myRange.location
NSLog("%d", loc);
I know this is old and an answer has been accepted, but you can find the index of the string in a couple lines of code using:
var str : String = "abcdefghi"
let characterToFind: Character = "c"
let characterIndex = find(str, characterToFind) //returns 2
Some other great information about Swift strings here Strings in Swift
Variable type String in Swift contains different functions compared to NSString in Objective-C . And as Sulthan mentioned,
Swift String doesn't implement RandomAccessIndex
What you can do is downcast your variable of type String to NSString (this is valid in Swift). This will give you access to the functions in NSString.
var str = "abcdefghi" as NSString
str.rangeOfString("c").locationx // returns 2
If you think about it, you actually don't really need the exact Int version of the location. The Range or even the String.Index is enough to get the substring out again if needed:
let myString = "hello"
let rangeOfE = myString.rangeOfString("e")
if let rangeOfE = rangeOfE {
myString.substringWithRange(rangeOfE) // e
myString[rangeOfE] // e
// if you do want to create your own range
// you can keep the index as a String.Index type
let index = rangeOfE.startIndex
myString.substringWithRange(Range<String.Index>(start: index, end: advance(index, 1))) // e
// if you really really need the
// Int version of the index:
let numericIndex = distance(index, advance(index, 1)) // 1 (type Int)
}
The Simplest Way is:
In Swift 3:
var textViewString:String = "HelloWorld2016"
guard let index = textViewString.characters.index(of: "W") else { return }
let mentionPosition = textViewString.distance(from: index, to: textViewString.endIndex)
print(mentionPosition)
String is a bridge type for NSString, so add
import Cocoa
to your swift file and use all the "old" methods.
In terms of thinking this might be called an INVERSION. You discover the world is round instead of flat. "You don't really need to know the INDEX of the character to do things with it." And as a C programmer I found that hard to take too!
Your line "let index = letters.characters.indexOf("c")!" is enough by itself.
For example to remove the c you could use...(playground paste in)
var letters = "abcdefg"
//let index = letters.rangeOfString("c")!.startIndex //is the same as
let index = letters.characters.indexOf("c")!
range = letters.characters.indexOf("c")!...letters.characters.indexOf("c")!
letters.removeRange(range)
letters
However, if you want an index you need to return an actual INDEX not an Int as an Int value would require additional steps for any practical use. These extensions return an index, a count of a specific character, and a range which this playground plug-in-able code will demonstrate.
extension String
{
public func firstIndexOfCharacter(aCharacter: Character) -> String.CharacterView.Index? {
for index in self.characters.indices {
if self[index] == aCharacter {
return index
}
}
return nil
}
public func returnCountOfThisCharacterInString(aCharacter: Character) -> Int? {
var count = 0
for letters in self.characters{
if aCharacter == letters{
count++
}
}
return count
}
public func rangeToCharacterFromStart(aCharacter: Character) -> Range<Index>? {
for index in self.characters.indices {
if self[index] == aCharacter {
let range = self.startIndex...index
return range
}
}
return nil
}
}
var MyLittleString = "MyVery:important String"
var theIndex = MyLittleString.firstIndexOfCharacter(":")
var countOfColons = MyLittleString.returnCountOfThisCharacterInString(":")
var theCharacterAtIndex:Character = MyLittleString[theIndex!]
var theRange = MyLittleString.rangeToCharacterFromStart(":")
MyLittleString.removeRange(theRange!)
Swift 4 Complete Solution:
OffsetIndexableCollection (String using Int Index)
https://github.com/frogcjn/OffsetIndexableCollection-String-Int-Indexable-
let a = "01234"
print(a[0]) // 0
print(a[0...4]) // 01234
print(a[...]) // 01234
print(a[..<2]) // 01
print(a[...2]) // 012
print(a[2...]) // 234
print(a[2...3]) // 23
print(a[2...2]) // 2
if let number = a.index(of: "1") {
print(number) // 1
print(a[number...]) // 1234
}
if let number = a.index(where: { $0 > "1" }) {
print(number) // 2
}
extension String {
//Fucntion to get the index of a particular string
func index(of target: String) -> Int? {
if let range = self.range(of: target) {
return characters.distance(from: startIndex, to: range.lowerBound)
} else {
return nil
}
}
//Fucntion to get the last index of occurence of a given string
func lastIndex(of target: String) -> Int? {
if let range = self.range(of: target, options: .backwards) {
return characters.distance(from: startIndex, to: range.lowerBound)
} else {
return nil
}
}
}
You can find the index number of a character in a string with this:
var str = "abcdefghi"
if let index = str.firstIndex(of: "c") {
let distance = str.distance(from: str.startIndex, to: index)
// distance is 2
}
If you are looking for easy way to get index of Character or String checkout this library http://www.dollarswift.org/#indexof-char-character-int
You can get the indexOf from a string using another string as well or regex pattern
To get index of a substring in a string with Swift 2:
let text = "abc"
if let range = text.rangeOfString("b") {
var index: Int = text.startIndex.distanceTo(range.startIndex)
...
}
In swift 2.0
var stringMe="Something In this.World"
var needle="."
if let idx = stringMe.characters.indexOf(needle) {
let pos=stringMe.substringFromIndex(idx)
print("Found \(needle) at position \(pos)")
}
else {
print("Not found")
}
let mystring:String = "indeep";
let findCharacter:Character = "d";
if (mystring.characters.contains(findCharacter))
{
let position = mystring.characters.indexOf(findCharacter);
NSLog("Position of c is \(mystring.startIndex.distanceTo(position!))")
}
else
{
NSLog("Position of c is not found");
}
I play with following
extension String {
func allCharactes() -> [Character] {
var result: [Character] = []
for c in self.characters {
result.append(c)
}
return
}
}
until I understand the provided one's now it's just Character array
and with
let c = Array(str.characters)
If you only need the index of a character the most simple, quick solution (as already pointed out by Pascal) is:
let index = string.characters.index(of: ".")
let intIndex = string.distance(from: string.startIndex, to: index)
On the subject of turning a String.Index into an Int, this extension works for me:
public extension Int {
/// Creates an `Int` from a given index in a given string
///
/// - Parameters:
/// - index: The index to convert to an `Int`
/// - string: The string from which `index` came
init(_ index: String.Index, in string: String) {
self.init(string.distance(from: string.startIndex, to: index))
}
}
Example usage relevant to this question:
var testString = "abcdefg"
Int(testString.range(of: "c")!.lowerBound, in: testString) // 2
testString = "π¨π¦πΊπΈπ©πͺπ©βπ©βπ§βπ¦\u{1112}\u{1161}\u{11AB}"
Int(testString.range(of: "π¨π¦πΊπΈπ©πͺ")!.lowerBound, in: testString) // 0
Int(testString.range(of: "π©βπ©βπ§βπ¦")!.lowerBound, in: testString) // 1
Int(testString.range(of: "αα
‘α«")!.lowerBound, in: testString) // 5
Important:
As you can tell, it groups extended grapheme clusters and joined characters differently than String.Index. Of course, this is why we have String.Index. You should keep in mind that this method considers clusters to be singular characters, which is closer to correct. If your goal is to split a string by Unicode codepoint, this is not the solution for you.
In Swift 2.0, the following function returns a substring before a given character.
func substring(before sub: String) -> String {
if let range = self.rangeOfString(sub),
let index: Int = self.startIndex.distanceTo(range.startIndex) {
return sub_range(0, index)
}
return ""
}
As my perspective, The better way with knowing the logic itself is below
let testStr: String = "I love my family if you Love us to tell us I'm with you"
var newStr = ""
let char:Character = "i"
for value in testStr {
if value == char {
newStr = newStr + String(value)
}
}
print(newStr.count)
I have a problem in which I have to SWAP or move characters and integers. Like I have any characters A . now I have some cases, like
NOTE:- Have to use characters A-Z and integers 0-9
A, now I want that when my program run I assign some integer value to this character, If I assign value 3 to this character then A will become D or it just move to 3 places.
Now if I have a character like Y and I add 4 then it will become C means after Z it will again start from character A.
Same condition I have to follow with Integer if i have 9 and we assign 3 to it then it will become 2 because loop start from 0 not from 1. Means we have to use only 0-9 integers.
I know that i am using wrong name to question but i have no idea that what lines i have to use for that kind of question.
Hope you understand my problem.
Thanks in advance.
Try the below extension method, which does the following:
It creates 2 dictionaries in order to speed up the key look up in the alphabet
Will parse the inputString variable, split it in substrings of the length of the moveString variable's length (or the remainder)
On every substring, it will evaluate each character in order to detect if it's a digit
If it's not a digit, it looks up for the value in the swappedAlphabet dictionary, by using the int key
If it's a digit, it applies a modulo operation on the sum of the digit and the corresponding moveint value
It finally aggregates all the characters in the final result string
Here's the code:
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static void Main(string[] args)
{
string
alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
string inputString = "ABC123D", moveString = "12";
var result = inputString.Swap(alphabet, moveString);
Console.WriteLine(result);
}
}
static class ExtensionMethods
{
public static Dictionary<TValue, TKey>
SwapKeysValues<TKey, TValue>(this Dictionary<TKey, TValue> input)
{
var result = new Dictionary<TValue, TKey>();
input.ToList().ForEach((keyValuePair) =>
{
result.Add(keyValuePair.Value, keyValuePair.Key);
});
return result;
}
public static string Swap(
this string input,
string alphabet,
string move)
{
Dictionary<char, int>
alphabetDictionary = new Dictionary<char, int>();
for (int i = 0; i < alphabet.Length; i++)
{
alphabetDictionary.Add(alphabet[i], i);
}
var swapedAlphabet = alphabetDictionary.SwapKeysValues();
return Enumerable
.Range(0, (int)Math.Ceiling(input.Length / (move.Length * 1M)))
.ToList()
.Aggregate<int, string>("", (s, i) =>
{
var l = i * move.Length + move.Length;
var cInput = input.Substring(i * move.Length,
(l > input.Length)
? input.Length - i * move.Length : move.Length);
return s + cInput
.Select((c, index) =>
{
int intCandidate;
if (!Int32.TryParse(c.ToString(), out intCandidate))
{
var length = (alphabetDictionary[c] +
Int32.Parse(move[index].ToString()));
return
swapedAlphabet[(alphabet.Length > length)
? length : length % alphabet.Length];
}
else
{
var moveInt = Int32.Parse(move[index].ToString());
return Char.Parse(((intCandidate + moveInt) % 10)
.ToString());
}
})
.Aggregate<char, string>("", (a, b) => a + b);
});
}
}
Another alternative you have is relying on the in-built character/integer types which follow the order you want; with an additional consideration: if you account for caps, it would deliver caps ("B" after "A" and "b" after "a"). The only thing you need to worry about is making sure that the iterations will be limited to the A-Z/0-9 boundaries. Sample code:
public string moveChar(string inputChar, int noPos)
{
string outChar = checkBoundaries(inputChar, noPos);
if (outChar == "")
{
outChar = basicConversion(inputChar, noPos);
}
return outChar;
}
public string basicConversion(string inputChar, int noPos)
{
return Convert.ToString(Convert.ToChar(Convert.ToInt32(Convert.ToChar(inputChar)) + noPos));
}
public string checkBoundaries(string inputChar, int noPos)
{
string outString = "";
int count1 = 0;
do
{
count1 = count1 + 1;
string curTemp = basicConversion(inputChar, 1);
if (inputChar.ToLower() == "z" || curTemp.ToLower() == "z")
{
if (inputChar.ToLower() != "z")
{
noPos = noPos - count1;
}
inputChar = "a";
outString = "a";
if (inputChar == "Z" || curTemp == "Z")
{
inputChar = "A";
outString = "A";
}
count1 = 1;
}
else if (inputChar == "9" || curTemp == "9")
{
if (inputChar != "9")
{
noPos = noPos - count1;
}
inputChar = "0";
outString = "0";
count1 = 1;
}
else
{
inputChar = curTemp;
outString = inputChar;
}
} while (count1 < noPos);
return outString;
}
It expects strings (just one character (letter or number) per call) and you can call it simply by using: moveChar("current letter or number", no_of_pos_to_move). This version accounts just for "positive"/"forwards" movements but it might easily be edited to account for the inverse situation.
Here's a very simple way to implement a Caesar Cipher with the restrictions you defined.
var shift = 3;
var input = "HELLO WORLD 678";
var classAlphabets = new Dictionary<UnicodeCategory, string>
{
{ UnicodeCategory.SpaceSeparator, " " },
{ UnicodeCategory.UppercaseLetter, "ABCDEFGHIJKLMNOPQRSTUVWXYZ" },
{ UnicodeCategory.DecimalDigitNumber, "0123456789" }
};
var encoded = input.ToUpperInvariant()
.Select(c => new { Alphabet = classAlphabets[Char.GetUnicodeCategory(c)], Character = c })
.Select(x => new { x.Alphabet, Index = x.Alphabet.IndexOf(x.Character) })
.Select(x => new { x.Alphabet, Index = x.Index + shift })
.Select(x => new { x.Alphabet, Index = x.Index % x.Alphabet.Length })
.Select(x => x.Alphabet.ElementAt(x.Index))
.Aggregate(new StringBuilder(), (builder, character) => builder.Append(character))
.ToString();
Console.Write(encoded);
// encoded = "KHOOR ZRUOG 901"
Decoding is simply a case of inverting the shift.
Caesar cipher can be easier like this:
static char Encrypt(char ch, int code)
{
if (!char.IsLetter(ch))
{
return ch;
}
char offset = char.IsUpper(ch) ? 'A' : 'a';
return (char)(((ch + code - offset) % 26) + offset);
}
static string Encrypt(string input, int code)
{
return new string(input.ToCharArray().Select(ch => Encrypt(ch, code)).ToArray());
}
static string Decrypt(string input, int code)
{
return Encrypt(input, 26 - code);
}
const string TestCase = "Pack my box with five dozen liquor jugs.";
static void Main()
{
string str = TestCase;
Console.WriteLine(str);
str = Encrypt(str, 5);
Console.WriteLine("Encrypted: {0}", str);
str = Decrypt(str, 5);
Console.WriteLine("Decrypted: {0}", str);
Console.ReadKey();
}