String componentsSeparatedByString do one time - ios

I have a string:
let mystring = "key=value=value=value=value"
When i did:
let ar = mystring.componentsSeparatedByString("=")
i get:
["key", "value", "value", "value", "value"]
but i need do split only once, like componentsSeparatedByString("=", 1), to get:
["key", "value=value=value=value"]

With Swift 2.1, you can use the split function as follows to do what you want:
let result = string.characters.split("=", maxSplit: 1, allowEmptySlices: true)
Some example code to test this would be:
let string = "key=value=value=value=value"
let result = string.characters.split("=", maxSplit: 1, allowEmptySlices: true)
print(String(result[0])) // "key"
print(String(result[1])) // "value=value=value=value"

This should do the job
func extract(rawData: String) -> [String]? {
let elms = rawData.characters.split("=", maxSplit: 1).map { String($0) }
guard let
key = elms.first,
value = elms.last
where elms.count == 2 else { return nil }
return [key, value]
}
Example:
let rawData = "key=value=value=value=value"
extract(rawData) // > ["key", "value=value=value=value"]
Please note the extract function does an optional array of strings. Infact if the input string does not contain at least an = then nil is returned.
The code has been tested with the Swift 2.1 and Xcode Playground 7.1.1.
Hope this helps.

You're probably going to have to write your own custom code to do that, using either NSScanner or rangeofString:options:range:
EDIT:
Actually, it sounds like the Swift String class's split function, with its maxSplit parameter, will do what you need. Take a look at the link in Preston's answer.

let mystring = "key=value=value=value=value"
let result = split(mystring as String, { $0 == "=" }, maxSplit: 1, allowEmptySlices: true)
result should now be [key, value=value=value=value]

Thanks for answers, i found working solution for swift2:
let mystring = "key=value=value=value=value"
mystring.characters.split(1, allowEmptySlices: true, isSeparator: { $0 == "=" }).map(String.init)

Try this: (tested and working in playground)
var key = str.substringToIndex(str.rangeOfString("=")!.startIndex)
var value = str.substringFromIndex(str.rangeOfString("=")!.startIndex.advancedBy(1))
var resultingArray = [key, value]

Related

How to split the string from an array of strings in swift

I have an array like :
dateTime = ["2018/06/25 05:32:30","2018/05/25 02:37","2018/04/25 05:32:50","2018/07/25 06:30:30"]
Need to split the strings and get the response as :
time = ["05:32:30","02:37","05:32:50","06:30:30"]
Can anyone please help on this.
you can use flatMap:
let dates = ["2018/06/25 05:32:30","2018/05/25 02:37","2018/04/25 05:32:50","2018/07/25 06:30:30"]
let times = dates.flatMap({ $0.split(separator: " ").last ?? nil })
print(times)
// prints: ["05:32:30", "02:37", "05:32:50", "06:30:30"]
Use this below solution to get the times from all elements.
let dateTime = ["2018/06/25 05:32:30","2018/05/25 02:37","2018/04/25 05:32:50","2018/07/25 06:30:30"]
let array = dateTime.map { $0.components(separatedBy: " ")[1] }
//["05:32:30", "02:37", "05:32:50", "06:30:30"]
Loops each items & use split function
let times = dateTime.compactMap { $0.split(separator: " ").last }

How to remove 1 component in a string? Swift

I have this string "01:07:30" and I would like to remove the zero in "01" and keep everything else the same. My final string should look like this
"1:07:30"
Is there a way to do so in Swift? Thank you so much!
Try the following. This will create a string, check to see if the first character is 0 if it is remove it. Otherwise do nothing.
var myString = "01:07:30"
if myString.first == "0" {
myString.remove(at: myString.startIndex)
}
print(myString) // 1:07:30
The final result is the string will now be 1:07:30 instead of 01:07:30.
If I'm best understanding your question, you want to replace the occurrences of "01" with "1" in your string, so you can use regex or the string functionality it self
simply:
let searchText = "01"
let replaceWithValue = "1"
let string = "01:07:08:00:01:01"
let newString = string.replacingOccurrences(of: searchText, with: replaceWithValue) // "1:07:08:00:1:1"
If you want to replace the fist occurrence only, simply follow this answer:
https://stackoverflow.com/a/33822186/3911553
If you just want to deal with string here is one of many solution:
var myString = "01:07:30"
let list = myString.components(separatedBy: ":")
var finalString = ""
for var obj in list{
if obj.first == "0" {
obj.removeFirst()
}
finalString += finalString.count == 0 ? "\(obj)" : ":\(obj)"
}
print(finalString)

If let condition true when value is missing in optional type, swift

I have parser in Objc, parser returns NSDictionary. I am using this parser in swift class. But when some value is missing on that dictionary, it shows nil value. e.g. ->
wirlessData = {
"anon" = {
};
"channel" = {
"text" = 1;
};
}
I am checking through
if let wepauthValue = wirlessData["wepauth"] {
if let value = wepauthValue["text"] {
print("\(value)") // nil
}
}
I don't how it satisfy the if let condition. Any one faced this types of problem can help me out.
Thanks,
vikash
You don't need any special code to do this, because it is what a dictionary already does. When you fetch dict[key] you know whether the dictionary contains the key, because the Optional that you get back is not nil (and it contains the value).
So, if you just want to answer the question whether the dictionary contains the key, ask:
let keyExists = dict[key] != nil
If you want the value and you know the dictionary contains the key, say:
let val = dict[key]!
But if, as usually happens, you don't know it contains the key - you want to fetch it and use it, but only if it exists - then use something like if let:
if let val = dict[key] {
// now val is not nil and the Optional has been unwrapped, so use it
}
I have tested it and found that value is still optional.Take a look at screenshot below to understand it better.
"anon" would be an empty dictionary. An empty dictionary is not nil, it is a dictionary. Just an empty one. A JSON parser will never, ever give nil values unless you ask for a key that is not in a dictionary. For example wirlessData ["nonexistingkey"] would give you nil.
If you be more type-strong about it with the if..let's then:
if let anonValue = wirlessData["anon"] {
if let value = anonValue["text"] as? String {
// This won't execute if value isn't converted from `anonvalue["text"]` to String specifically. This includes null been a false match too
print("\(value)") // nil
}else{
print("Value did't match string at all")
}
}
or even more specifically in your case:
if let anonValue = wirlessData["anon"] {
if let value = anonValue["text"] as? Int {
// This won't execute if value isn't converted from `anonvalue["text"]` to String specifically. This includes null been a false match too
print("\(value)") // nil
}else{
print("Value did't match int at all")
}
}
The value your parser is returning not nil, its empty so you need to check on count if inner data type is dictionary or array, I have past 1 sample here
Please use below code and correct your logic accordingly to get it work properly
let wirlessData:[String:AnyObject] = [
"anon" : [],
"channel" : [
"text" : 1
]
]
if wirlessData["anon"]?.count > 0 {
if let value = wirlessData["anon"]!["text"] {
print("\(value)") // nil
}
}
Try this below code using type check operator (is) -
if wirlessData["anon"] is [String:AnyObject]
{
let anon = wirlessData["anon"]!
print(anon)
if anon["random"] is String {
let stringValue = anon["random"]!
print("\(stringValue)")
}
else if anon["random"] is Int
{
let intValue = anon["random"]!
print("\(intValue)") // nil
}
else
{
print(" may be value did't match string & Int or nil ")
}
}

Splitting a Swift String using a multi-Character String in Swift 2

I understand that I can fallback to the NSString function componentsSeparatedByString, and so perhaps this is a nitpick, but one of the things I like about Swift is that it is designed around brevity and short syntax.
I was really hoping I could just: var parts = myString.characters.split("${") but that function only works for a single Character, not a two Character string. I even tried var parts = myString.characters.split { $0 == "${" } but that is expecting a single Character as the delimiter and not a full String. :(
Is there an api function that I'm missing or do I need to stick with the the old NSString bridged functions?
Here's a rather simple-minded approach that makes it possible to use Swift split on a single character:
extension String {
mutating func replace(target:String, with:String) {
while let r = self.rangeOfString(target) {
self.replaceRange(r, with: with)
}
}
func split(separator:String) -> Array<String> {
var s = self
s.replace(separator, with:"☞") // arbitrary improbable character :)
return s.characters.split("☞").map{String($0)}
}
}
var s = "the${cat${sat${on${the${mat"
let arr = s.split("${")
However, rangeOfString is actually a Foundation method on NSString; if you don't import Foundation (or UIKit), that code won't compile. So in reality it's no improvement over just calling componentsSeparatedByString. I don't actually understand your objection to it in the first place; Swift has holes exactly because it expects Foundation to be backing it up and filling those holes.
'pure' Swift's solution where import Foundation is NOT required and arbitrary improbable character doesn't exists
let str = "t{he${cat${sat${on${the${mat"
let splitBy = "${"
extension String {
func split(splitBy: String)->[String] {
if self.isEmpty { return [] }
var arr:[String] = []
var tmp = self
var tmp1 = ""
var i = self.startIndex
let e = self.endIndex
let c = splitBy.characters.count
while i < e {
let tag = tmp.hasPrefix(splitBy)
if !tag {
tmp1.append(tmp.removeAtIndex(tmp.startIndex))
i = i.successor()
} else {
tmp.removeRange(Range(start: tmp.startIndex, end: tmp.startIndex.advancedBy(c)))
i = i.advancedBy(c)
arr.append(tmp1)
tmp1 = ""
}
}
arr.append(tmp1)
return arr.filter{ !$0.isEmpty }
}
}
let arr = str.split(splitBy) // ["t{he", "cat", "sat", "on", "the", "mat"]
If you have Foundation imported, you can use the components(separatedBy:) method to accomplish that.
let str = "Foo, Bar, Baz"
str.components(separatedBy: ", ")
Here are the docs.
(Tested on Ubuntu Linux)

Swift filter array of strings

I've had troubles filtering array of keywords (strings) in swift ,My code:
self.filteredKeywords=filter(keywords.allValues, {(keyword:NSString) ->
Bool in
let words=keyword as? NSString
return words?.containsString(searchText)
})
As AnyObject can't be subtype of NSString, I'm stuck with this!
[Updated for Swift 2.0]
As NSString is toll-free bridged to Swift String, just avoid the coercions with:
3> ["abc", "bcd", "xyz"].filter() { nil != $0.rangeOfString("bc") }
$R1: [String] = 2 values {
[0] = "abc"
[1] = "bcd"
}
But, if you think allValues aren't strings:
(keywords.allValues as? [String]).filter() { nil != $0.rangeOfString("bc") }
which returns an optional array.
Your filter is over [AnyObject], but your closure takes NSString. These need to match. Also, your result needs to be a Bool, not a Bool?. You can address these simply like this:
self.filteredKeywords = filter(keywords.allValues, {
let keyword = $0 as? NSString
return keyword?.containsString(searchText) ?? false
})
This accepts AnyObject and then tries to coerce it down to NSString. It then nil-coalleces (??) the result to make sure it always is a Bool.
I'd recommend, though, treating keywords as a [String:String] rather than an NSDictionary. That would get rid of all the complications of AnyObject. Then you can just do this:
self.filteredKeywords = keywords.values.filter { $0.rangeOfString(searchText) != nil }
Whenever possible, convert Foundation collections into Swift collections as soon as you can and store those. If you have incoming Foundation objects, you can generally convert them easily with techniques like:
let dict = nsdict as? [String:String] ?? [:]
Or you can do the following to convert them such that they'll crash in debug (but silently "work" in release):
func failWith<T>(msg: String, value: T) -> T {
assertionFailure(msg)
return value
}
let dict = nsdict as? [String:String] ?? failWith("Couldn't convert \(d)", [:])
Swift 4.2 provides a new way to do this:
var theBigLebowski = ["The Dude", "Angry Walter", "Maude Lebowski", "Donny Kerabatsos", "The Big Lebowski", "Little Larry Sellers"]
// after removeAll -> ["The Dude", "Angry Walter", "Donny Kerabatsos", "Little Larry Sellers"]
theBigLebowski.removeAll{ $0.contains("Lebowski")}
print(theBigLebowski)
There is both a problem with GoZoner's answer for certain data types and also a slightly better way to do this. The following examples can show this:
let animalArray: NSMutableArray = ["Dog","Cat","Otter","Deer","Rabbit"]
let filteredAnimals = animalArray.filter { $0.rangeOfString("er") != nil }
print("filteredAnimals:", filteredAnimals)
filteredAnimals: [Dog, Cat, Otter, Deer, Rabbit]
Likely not the set you expected!
However this works fine this way if we don't type animalArray as an NSMutableArray:
let animalArray = ["Dog","Cat","Otter","Deer","Rabbit"]
let filteredAnimals = animalArray.filter { $0.rangeOfString("er") != nil }
print("filteredAnimals:", filteredAnimals)
filteredAnimals: [Otter, Deer]
However I'd recommend using $0.contains() instead of $0.rangeOfString() != nil because it functions in both circumstances and slightly enhances the readability of the code:
let animalArray: NSMutableArray = ["Dog","Cat","Otter","Deer","Rabbit"]
let filteredAnimals = animalArray.filter { $0.contains("er") }
print("filteredAnimals:", filteredAnimals)
filteredAnimals: [Otter, Deer]

Resources