extract the first word from a string - regex - regex

I have the following string:
str1 = "cat-one,cat2,cat-3";
OR
str1 = "catone,cat-2,cat3";
OR
str1 = "catone";
OR
str1 = "cat-one";
The point here is words may/may not have "-"s in it
Using regex:
How could I extract the 1st word?
Appreciate any help on this.
Thanks,
L

It's pretty easy, just include allowed characters in brackets:
^([\w\-]+)

An approach not using a regex: assuming the first word is delimited always by a comma "," you can do this:
var str1 = "cat-one";
var i = str1.indexOf(",");
var firstTerm = i == -1 ? str1 : str1.substring(0, i);
Edit: Assumed this was a javascript question, for some reason.

If someone, one day would like to do it in Swift here you go with an extension :
extension String {
func firstWord() -> String? {
var error : NSError?
let internalExpression = NSRegularExpression(pattern: "^[a-zA-Z0-9]*", options: .CaseInsensitive, error: &error)!
let matches = internalExpression.matchesInString(self, options: nil, range:NSMakeRange(0, countElements(self)))
if (matches.count > 0) {
let range = (matches[0] as NSTextCheckingResult).range
return (self as NSString).substringWithRange(range)
}
return nil
}
}
To use it just write:
myString.firstWord()

Related

Most efficient way to remove leading zeros from Swift 3 string

I have a string such as "00123456" that I would like to have in an string "123456", with the leading zeros removed.
I've found several examples for Objective-C but not sure best way to do so with Swift 3.
Thanks
You can do that with Regular Expression
let string = "00123456"
let trimmedString = string.replacingOccurrences(of: "^0+", with: "", options: .regularExpression)
The benefit is no double conversion and no force unwrapping.
Just convert the string to an int and then back to a string again. It will remove the leading zeros.
let numberString = "00123456"
let numberAsInt = Int(numberString)
let backToString = "\(numberAsInt!)"
Result: "123456"
First, create Validator then use it in any class.
This is an example and it works :)
This is swift 4.0
class PhoneNumberExcludeZeroValidator {
func validate(_ value: String) -> String {
var subscriberNumber = value
let prefixCase = "0"
if subscriberNumber.hasPrefix(prefixCase) {
subscriberNumber.remove(at: subscriberNumber.startIndex)
}
return subscriberNumber
}
}
example for usage:
if let countryCallingCode = countryCallingCodeTextField.text, var subscriberNumber = phoneNumberTextField.text {
subscriberNumber = PhoneNumberExcludeZeroValidator().validate(subscriberNumber)
let phoneNumber = "\(countryCallingCode)\(subscriberNumber)"
registerUserWith(phoneNumber: phoneNumber)
}
let number = "\(String(describing: Int(text)!))"

Without using NSRegularExpression, How can I get all matches of my string regular expression?

Swift 3 introduced String.range(of:options). Then, with this function, is possible match a part of string without creating a NSRegularExpression object, for example:
let text = "it is need #match my both #hashtag!"
let match = text.range(of: "(?:^#|\\s#)[\\p{L}0-9_]*", options: .regularExpression)!
print(text[match]) // #math
But, is possible match both occurrences of the regexp (that is, #match and #hashtag), instead of only the first?
let text = "it is need #match my both #hashtag!"
// create an object to store the ranges found
var ranges: [Range<String.Index>] = []
// create an object to store your search position
var start = text.startIndex
// create a while loop to find your regex ranges
while let range = text.range(of: "(?:^#|\\s#)[\\p{L}0-9_]*", options: .regularExpression, range: start..<text.endIndex) {
// append your range found
ranges.append(range)
// and change the startIndex of your string search
start = range.lowerBound < range.upperBound ? range.upperBound : text.index(range.lowerBound, offsetBy: 1, limitedBy: text.endIndex) ?? text.endIndex
}
ranges.forEach({print(text[$0])})
This will print
#match
#hashtag
If you need to use it more than once in your code you should add this extension to your project:
extension StringProtocol {
func ranges<S: StringProtocol>(of string: S, options: String.CompareOptions = []) -> [Range<Index>] {
var result: [Range<Index>] = []
var start = startIndex
while start < endIndex,
let range = self[start...].range(of: string, options: options) {
result.append(range)
start = range.lowerBound < range.upperBound ?
range.upperBound : index(after: range.lowerBound)
}
return result
}
}
usage:
let text = "it is need #match my both #hashtag!"
let pattern = "(?<!\\S)#[\\p{L}0-9_]*"
let ranges = text.ranges(of: pattern, options: .regularExpression)
let matches = ranges.map{text[$0]}
print(matches) // ["#match", "#hashtag"]

Search array element in string swift 3.0

I want to search an element of an array of strings in a string. Like this:
let array:[String] = ["dee", "kamal"]
let str:String = "Hello all how are you, I m here for deepak."
so, I want
str.contain("dee") == true
any possible search in string?
You can do it in one line by composing a regular expression pattern "(item1|item2|item3)"
let array = ["dee", "kamal"]
let str = "Hello all how are you, I m here for deepak."
let success = str.range(of: "(" + array.joined(separator: "|") + ")", options: .regularExpression) != nil
You should iterate over the array and for each element, call str.contains.
for word in array {
if str.contains(word) {
print("\(word) is part of the string")
} else {
print("Word not found")
}
}
You can do like this:
array.forEach { (item) in
var isContains:Bool = str.contains(item)
print(isContains)
}

Make sure regex matches the entire string with Swift regex

How to check whether a WHOLE string can be matches to regex? In Java is method String.matches(regex)
You need to use anchors, ^ (start of string anchor) and $ (end of string anchor), with range(of:options:range:locale:), passing the .regularExpression option:
import Foundation
let phoneNumber = "123-456-789"
let result = phoneNumber.range(of: "^\\d{3}-\\d{3}-\\d{3}$", options: .regularExpression) != nil
print(result)
Or, you may pass an array of options, [.regularExpression, .anchored], where .anchored will anchor the pattern at the start of the string only, and you will be able to omit ^, but still, $ will be required to anchor at the string end:
let result = phoneNumber.range(of: "\\d{3}-\\d{3}-\\d{3}$", options: [.regularExpression, .anchored]) != nil
See the online Swift demo
Also, using NSPredicate with MATCHES is an alternative here:
The left hand expression equals the right hand expression using a regex-style comparison according to ICU v3 (for more details see the ICU User Guide for Regular Expressions).
MATCHES actually anchors the regex match both at the start and end of the string (note this might not work in all Swift 3 builds):
let pattern = "\\d{3}-\\d{3}-\\d{3}"
let predicate = NSPredicate(format: "self MATCHES [c] %#", pattern)
let result = predicate.evaluate(with: "123-456-789")
What you are looking for is range(of:options:range:locale:) then you can then compare the result of range(of:option:) with whole range of comparing string..
Example:
let phoneNumber = "(999) 555-1111"
let wholeRange = phoneNumber.startIndex..<phoneNumber.endIndex
if let match = phoneNumber.range(of: "\\(?\\d{3}\\)?\\s\\d{3}-\\d{4}", options: .regularExpression), wholeRange == match {
print("Valid number")
}
else {
print("Invalid number")
}
//Valid number
Edit: You can also use NSPredicate and compare your string with evaluate(with:) method of its.
let pattern = "^\\(?\\d{3}\\)?\\s\\d{3}-\\d{4}$"
let predicate = NSPredicate(format: "self MATCHES [c] %#", pattern)
if predicate.evaluate(with: "(888) 555-1111") {
print("Valid")
}
else {
print("Invalid")
}
Swift extract regex matches
with little bit of edit
import Foundation
func matches(for regex: String, in text: String) -> Bool {
do {
let regex = try NSRegularExpression(pattern: regex)
let nsString = text as NSString
let results = regex.matches(in: text, range: NSRange(location: 0, length: nsString.length))
return !results.isEmpty
} catch let error {
print("invalid regex: \(error.localizedDescription)")
return false
}
}
Example usage from link above:
let string = "19320"
let matched = matches(for: "^[1-9]\\d*$", in: string)
print(matched) // will match
let string = "a19320"
let matched = matches(for: "^[1-9]\\d*$", in: string)
print(matched) // will not match

Number of occurrences of substring in string in Swift

My main string is "hello Swift Swift and Swift" and substring is Swift.
I need to get the number of times the substring "Swift" occurs in the mentioned string.
This code can determine whether the pattern exists.
var string = "hello Swift Swift and Swift"
if string.rangeOfString("Swift") != nil {
println("exists")
}
Now I need to know the number of occurrence.
A simple approach would be to split on "Swift", and subtract 1 from the number of parts:
let s = "hello Swift Swift and Swift"
let tok = s.components(separatedBy:"Swift")
print(tok.count-1)
This code prints 3.
Edit: Before Swift 3 syntax the code looked like this:
let tok = s.componentsSeparatedByString("Swift")
Should you want to count characters rather than substrings:
extension String {
func count(of needle: Character) -> Int {
return reduce(0) {
$1 == needle ? $0 + 1 : $0
}
}
}
Optimising dwsolbergs solution to count faster. Also faster than componentsSeparatedByString.
extension String {
/// stringToFind must be at least 1 character.
func countInstances(of stringToFind: String) -> Int {
assert(!stringToFind.isEmpty)
var count = 0
var searchRange: Range<String.Index>?
while let foundRange = range(of: stringToFind, options: [], range: searchRange) {
count += 1
searchRange = Range(uncheckedBounds: (lower: foundRange.upperBound, upper: endIndex))
}
return count
}
}
Usage:
// return 2
"aaaa".countInstances(of: "aa")
If you want to ignore accents, you may replace options: [] with options: .diacriticInsensitive like dwsolbergs did.
If you want to ignore case, you may replace options: [] with options: .caseInsensitive like ConfusionTowers suggested.
If you want to ignore both accents and case, you may replace options: [] with options: [.caseInsensitive, .diacriticInsensitive] like ConfusionTowers suggested.
If, on the other hand, you want the fastest comparison possible and you can guarantee some canonical form for composed character sequences, then you may consider option .literal and it will only perform exact matchs.
Swift 5 Extension
extension String {
func numberOfOccurrencesOf(string: String) -> Int {
return self.components(separatedBy:string).count - 1
}
}
Example use
let string = "hello Swift Swift and Swift"
let numberOfOccurrences = string.numberOfOccurrencesOf(string: "Swift")
// numberOfOccurrences = 3
I'd recommend an extension to string in Swift 3 such as:
extension String {
func countInstances(of stringToFind: String) -> Int {
var stringToSearch = self
var count = 0
while let foundRange = stringToSearch.range(of: stringToFind, options: .diacriticInsensitive) {
stringToSearch = stringToSearch.replacingCharacters(in: foundRange, with: "")
count += 1
}
return count
}
}
It's a loop that finds and removes each instance of the stringToFind, incrementing the count on each go-round. Once the searchString no longer contains any stringToFind, the loop breaks and the count returns.
Note that I'm using .diacriticInsensitive so it ignore accents (for example résume and resume would both be found). You might want to add or change the options depending on the types of strings you want to find.
I needed a way to count substrings that may contain the start of the next matched substring. Leveraging dwsolbergs extension and Strings range(of:options:range:locale:) method I came up with this String extension
extension String
{
/**
Counts the occurrences of a given substring by calling Strings `range(of:options:range:locale:)` method multiple times.
- Parameter substring : The string to search for, optional for convenience
- Parameter allowOverlap : Bool flag indicating whether the matched substrings may overlap. Count of "🐼🐼" in "🐼🐼🐼🐼" is 2 if allowOverlap is **false**, and 3 if it is **true**
- Parameter options : String compare-options to use while counting
- Parameter range : An optional range to limit the search, default is **nil**, meaning search whole string
- Parameter locale : Locale to use while counting
- Returns : The number of occurrences of the substring in this String
*/
public func count(
occurrencesOf substring: String?,
allowOverlap: Bool = false,
options: String.CompareOptions = [],
range searchRange: Range<String.Index>? = nil,
locale: Locale? = nil) -> Int
{
guard let substring = substring, !substring.isEmpty else { return 0 }
var count = 0
let searchRange = searchRange ?? startIndex..<endIndex
var searchStartIndex = searchRange.lowerBound
let searchEndIndex = searchRange.upperBound
while let rangeFound = range(of: substring, options: options, range: searchStartIndex..<searchEndIndex, locale: locale)
{
count += 1
if allowOverlap
{
searchStartIndex = index(rangeFound.lowerBound, offsetBy: 1)
}
else
{
searchStartIndex = rangeFound.upperBound
}
}
return count
}
}
why not just use some length maths??
extension String {
func occurences(of search:String) -> Int {
guard search.count > 0 else {
preconditionFailure()
}
let shrunk = self.replacingOccurrences(of: search, with: "")
return (self.count - shrunk.count)/search.count
}
}
Try this
var mainString = "hello Swift Swift and Swift"
var count = 0
mainString.enumerateSubstrings(in: mainString.startIndex..<mainString.endIndex, options: .byWords) { (subString, subStringRange, enclosingRange, stop) in
if case let s? = subString{
if s.caseInsensitiveCompare("swift") == .orderedSame{
count += 1
}
}
}
print(count)
For the sake of completeness – and because there is a regex tag – this is a solution with Regular Expression
let string = "hello Swift Swift and Swift"
let regex = try! NSRegularExpression(pattern: "swift", options: .caseInsensitive)
let numberOfOccurrences = regex.numberOfMatches(in: string, range: NSRange(string.startIndex..., in: string))
The option .caseInsensitive is optional.
My solution, maybe it will be better to use String.Index instead of Int range but I think in such way it is a bit easier to read.
extension String {
func count(of char: Character, range: (Int, Int)? = nil) -> Int {
let range = range ?? (0, self.count)
return self.enumerated().reduce(0) {
guard ($1.0 >= range.0) && ($1.0 < range.1) else { return $0 }
return ($1.1 == char) ? $0 + 1 : $0
}
}
}
Solution which uses a higher order functions
func subStringCount(str: String, substr: String) -> Int {
{ $0.isEmpty ? 0 : $0.count - 1 } ( str.components(separatedBy: substr))
}
Unit Tests
import XCTest
class HigherOrderFunctions: XCTestCase {
func testSubstringWhichIsPresentInString() {
XCTAssertEqual(subStringCount(str: "hello Swift Swift and Swift", substr: "Swift"), 3)
}
func testSubstringWhichIsNotPresentInString() {
XCTAssertEqual(subStringCount(str: "hello", substr: "Swift"), 0)
}
}
Another way using RegexBuilder in iOS 16+ & swift 5.7+.
import RegexBuilder
let text = "hello Swift Swift and Swift"
let match = text.matches(of: Regex{"Swift"})
print(match.count) // prints 3
Using this as a function
func countSubstrings(string : String, subString : String)-> Int{
return string.matches(of: Regex{subString}).count
}
print(countSubstrings(string: text, subString: "Swift")) //prints 3
Using this as an Extension
extension String {
func countSubstrings(subString : String)-> Int{
return self.matches(of: Regex{subString}).count
}
}
print(text.countSubstrings(subString: "Swift")) // prints 3