I have a the following name: John Fitzgerald Kennedy.
To get its initials, I created a method:
extension String {
public var first: String {
return String(self[startIndex])
}
}
let initials = "John Fitzgerald Kennedy".componentsSeparatedByString(" ")
.reduce("") { $0 + $1.first }
The output is : JFK
Is there an elegant way with my method (with reduce) to limit those initials to JK only, removing then the letter in the middle?
If you target iOS 9 and above:
let formatter = PersonNameComponentsFormatter()
if let components = formatter.personNameComponents(from: name) {
formatter.style = .abbreviated
return formatter.string(from: components)
}
Swift 2.0
How about this:
let initials = "John Fitzgerald Kennedy".componentsSeparatedByString(" ")
.reduce("") { ($0 == "" ? "" : $0.first) + $1.first}
That will also take care of the case where the name has no middle name or more than 1 middle name.
Swift 3
let initials = "John Fitzgerald Kennedy".components(separatedBy: " ").reduce("") { ($0 == "" ? "" : "\($0.characters.first!)") + "\($1.characters.first!)" }
Swift 4
let initials = "John Fitzgerald Kennedy".components(separatedBy: " ").reduce("") { ($0 == "" ? "" : "\($0.first!)") + "\($1.first!)" }
Swift 4, little bit of extra work, but using PersonNameComponentsFormatter.
// Note: We Account for name suffix ( Example: III, II, Jr, Sr ) or prefixes ( Mr, Mrs )
let fullName = “Mr John Jacob Smith III”
let formatter = PersonNameComponentsFormatter()
guard let personNameComponents = formatter.personNameComponents(from: fullName) else {
return ""
}
return personNameComponents.initials
// Note: Also create Extension for PersonNameComponents
// PersonNameComponents+Initials.swift
import Foundation
extension PersonNameComponents {
var fullName: String {
return [givenName, middleName, familyName].compactMap{ $0 }.joined(separator: " ")
}
var fullNameWithSuffix: String {
return [givenName, middleName, familyName, nameSuffix].compactMap{ $0 }.joined(separator: " ")
}
var initials: String {
let firstName = givenName?.first ?? Character(" ")
let lastName = familyName?.first ?? Character(" ")
return "\(firstName)\(lastName)".trimmingCharacters(in: .whitespaces)
}
// Note: If You need first, middle, last
/*
var initials: String {
let firstName = givenName?.first ?? Character(" ")
let middleName = middleName?.first ?? Character(" ")
let lastName = familyName?.first ?? Character(" ")
return "\(firstName)\(middleName)\(lastName)".trimmingCharacters(in: .whitespaces)
}
*/
}
This is my solution.
Swift 5
extension String {
var initials: String {
return self.components(separatedBy: " ")
.reduce("") {
($0.isEmpty ? "" : "\($0.first?.uppercased() ?? "")") +
($1.isEmpty ? "" : "\($1.first?.uppercased() ?? "")")
}
}
}
extension String {
var initials: String {
return self.components(separatedBy: " ").filter { !$0.isEmpty }.reduce("") { ($0 == "" ? "" : "\($0.first!)") + "\($1.first!)" }
}
}
This fixes the crashing problem on Guy Daher's solution.
Enumerate and filter by odd index
let initials = "John Fitzgerald Kennedy".componentsSeparatedByString(" ")
.enumerate()
.filter { (index, _) -> Bool in return index % 2 == 0 }
.reduce("") { a, i in return a + i.element.first }
Note: This one is not care about no middle name or more than one
Related
I have React Native project. And I need in the native part of IOS to make string interpolation of formattedValue with a character, but I get the error "Type of expression is ambiguous without more context", and if I try the same code in the playground everything is working with out error. I'm not so experienced in SWIFT, can you tell me please why I get this error? And which the best way to make a string interpolation?
import Foundation
import SciChart.Protected.SCILabelProviderBase
class SCIAxisNumericLabelProvider: SCILabelProviderBase<ISCINumericAxis> {
var format: String?
var specialChar: String?
init(format: String?) {
let pattern = "[^0-9.]"
let regex = try! NSRegularExpression(pattern: pattern)
let formatValue = regex.stringByReplacingMatches(in: format ?? "", range: NSMakeRange(0, format?.count ?? 0), withTemplate: "")
self.format = formatValue
self.specialChar = format?.replacingOccurrences(of: "\(formatValue)", with: "")
super.init(axisType: ISCINumericAxis.self)
}
override func formatLabel(_ dataValue: ISCIComparable!) -> ISCIString! {
let dataValueToDouble = dataValue.toDouble()
let formattedValue = NSString(format: NSString(string: self.toFormat()), dataValueToDouble == -0 ? 0 : dataValueToDouble)
if let char = specialChar {
return "\(formattedValue) \(NSString(char))" // Here I get the error Type of expression is ambiguous without more context
}
return formattedValue
}
override func formatCursorLabel(_ dataValue: ISCIComparable!) -> ISCIString! {
return formatLabel(dataValue)
}
func toFormat() -> String {
if (self.format != nil) {
let a = self.format!.split(separator: ".")
if (a.count > 1) {
return "%0." + String(a[1].count) + "f"
}
}
return "%0.f"
}
func extractSpecialChar(value: String) {
}
}
In the end of the day I decided this error, I separated string interpolation and converting:
override func formatLabel(_ dataValue: ISCIComparable!) -> ISCIString! {
let formatterValue = self.toFormat();
let valueDouble = dataValue.toDouble()
let valueDoubleExcludedNegZero = valueDouble == -0 ? 0 : valueDouble
let precisionString = String(format: formatterValue, valueDoubleExcludedNegZero)
if let char = specialChar {
let resultString = precisionString + " " + char
return NSString(string: resultString)
}
return NSString(string: precisionString)
}
extension String {
var masked: String {
// some logic which I have to write to mask string.
// I tried following and just shows x 🤦♂️
// replacingOccurrences(
// of: ".(.+).",
// with: "x",
// options: .regularExpression,
// range: nil
//)
}
}
let helloWorld = "Hello World"
print("Masked string is - \(helloWorld.masked)")
Expected output is - "Hxxxxxxxxxd"
There is a Regular Expression way with lookaround
extension String {
var masked: String {
replacingOccurrences(
of: "(?!^).(?!$)", // RegEx
with: "x", // Replacement
options: .regularExpression // Option to set RegEx
)
}
}
You can enumerate the string and apply map transform to get the expected output:
extension String {
var masked: String {
self.enumerated().map({ (index, ch) in
if index == 0
|| index == self.count - 1 {
return String(ch)
}
return "x"
}).joined()
}
}
let str = "hello"
print("after masking \(str.masked)") // Output - hxxxo
The map transform will return an array, so use joined() to convert the array back to String. Also, note that you have to typecast ch to String as String(ch) because the type of ch is 'String.Element' (aka 'Character').
extension Sequence {
func replacingEachInteriorElement(with replacement: Element) -> [Element] {
let prefix = dropLast()
return
prefix.prefix(1)
+ prefix.dropFirst().map { _ in replacement }
+ suffix(1)
}
}
extension String {
var masked: Self {
.init( replacingEachInteriorElement(with: "x") )
}
}
"Hello World".masked == "Hxxxxxxxxxd" // true
"H🦾👄🐺🥻🐸🦈🏄♂️🍯🪐d".masked == "Hello World".masked // true
"🥮".masked // 🥮
"🥶😎".masked // 🥶😎
[].replacingEachInteriorElement(with: 500) // []
My solution without using Regular Expression:
extension String {
var masked: String {
if self.count < 2 { return self }
var output = self
let range = self.index(after: self.startIndex)..<self.index(before: endIndex)
let replacement = String.init(repeating: "x", count: output.count - 2)
output.replaceSubrange(range, with: replacement)
return output
}
}
So far, I've found following solution.
extension String {
var masked: String {
var newString = ""
for index in 0..<count {
if index != 0 && index != count-1 {
newString.append(contentsOf: "x")
} else {
let array = Array(self)
let char = array[index]
let string = String(char)
newString.append(string)
}
}
return newString
}
}
If you want to leave first and last letters you can use this ->
public extension String {
var masked: String {
return prefix(1) + String(repeating: "x", count: Swift.max(0, count-2)) + suffix(1)
}
}
USAGE
let hello = "Hello"
hello.masked
// Hxxxo
OR
you can pass unmasked character count ->
public extension String {
func masked(with unmaskedCount: Int) -> String {
let unmaskedPrefix = unmaskedCount/2
return prefix(unmaskedPrefix) + String(repeating: "x", count: Swift.max(0, count-unmaskedPrefix)) + suffix(unmaskedPrefix)
}
}
USAGE
let hello = "Hello"
hello.masked(with: 2)
// Hxxxo
let number = "5555555555"
number.masked(with: 4)
// 55xxxxxx55
Hello I have not get perfect solution for how to replace Email Id text before # with * like
suppose my email id is XXXXXXX#gmail.com then my output I want like *******#gmail.com
I try like this
let delimiter = "#"
let newstr = "Himmoradiya#gmail.com"
var token = newstr.components(separatedBy: delimiter)
let newQuery = (token[0] as NSString).replacingCharacters(
in: NSMakeRange(0,token[0].characters.count), with: "*")
Any suggestion is accepted.
Thank you.
Use init(repeating:count:)
let newstr = "Himmoradiya#gmail.com"
var token = newstr.components(separatedBy: "#")
let newQuery = String(repeating: "*", count: token[0].characters.count) //check array length before using index
print(newQuery + "#" + token[1] ) // *******#gmail.com
Write this code:
let aString = "This is my string"
let newString = aString.replacingOccurrences(of: "#", with: "*")
Use this function .
func getStarred(_ email : String ) -> String
{
var didFoundATRO = false
var tempString = ""
for char in email.characters
{
if char == "#"
{
didFoundATRO = true
}
if !didFoundATRO
{
tempString += "*"
}
else
{
tempString.append(char)
}
}
return tempString
}
You can do like,
let delimiter = "#"
let newstr = "Himmoradiya#gmail.com"
let arr = newstr.components(separatedBy: delimiter)
let resultStr = "xxxxxx" + "#" + arr[1]
print(resultStr)
let delimiter = "#"
var newstr = "Himmoradiya#gmail.com"
let arr = newstr.components(separatedBy: delimiter)
let strMail = arr.first!
let strReplaceStr = String(repeating: "*", count: strMail.characters.count)
newstr = newstr.replacingOccurrences(of: strMail, with: strReplaceStr)
and output (newstr) will be : ***********#gmail.com
I have created a string extension like...
extension String {
var hideEmailPrefix: String {
if self.contains("#") {
var part = self.components(separatedBy: "#")
let newText = String(repeating: "*", count: part[0].count)
return newText + "#" + part[1]
}
return self
}
}
Then in my ViewController:
override func viewDidLoad() {
super.viewDidLoad()
let email = "abc#gmail.com"
print(email.hideEmailPrefix) // ***#gmail.com
}
var maskEmail: String {
let email = self
let components = email.components(separatedBy: "#")
var maskEmail = ""
if let first = components.first {
maskEmail = String(first.enumerated().map { index, char in
return [0, 1, first.count - 1, first.count - 2].contains(index) ?
char : "*"
})
}
if let last = components.last {
maskEmail = maskEmail + "#" + last
}
return maskEmail
}
var maskPhoneNumber: String {
return String(self.enumerated().map { index, char in
return [0, 3, self.count - 1, self.count - 2].contains(index) ?
char : "*"
})
}
Here's what I am trying to do :
let courseName = "Bachelor of Tourism Administration(B.T.A)".condensedWhitespace
let upperCaseCourseName = courseName.uppercaseString
let extrctCourseName = upperCaseCourseName.componentsSeparatedByString(" ").reduce("") { $0.0 + String($0.1.characters.first!) }
let upperCasecourseFirstCharcters = extrctCourseName
print(upperCasecourseFirstCharcters) // output : "BOTA" but i want "BTA"
as you see that my outPut of "Bachelor of Tourism Administration(B.T.A)" is BOTA but the desired output is BTA because word of is starting from a lowerCase and i want to ignore that word in my this method , how am gonna do that any idea ?
let courseName = "Bachelor of Tourism Administration(B.T.A)" //.condensedWhitespace
var newString = ""
let array : NSArray = courseName.componentsSeparatedByString(" ")
for chr in array {
let str = chr as! NSString
if str.lowercaseString != str{
if newString.characters.count > 0{
newString = newString.stringByAppendingString(" "+(str as String))
continue
}
newString = newString.stringByAppendingString((str as String))
}
}
let upperCaseCourseName = newString.uppercaseString
let extrctCourseName = upperCaseCourseName.componentsSeparatedByString(" ").reduce("") { $0.0 + String($0.1.characters.first!) }
let upperCasecourseFirstCharcters = extrctCourseName
print(upperCasecourseFirstCharcters)
//This will defiantly meet to your problem/. Let me know if it works for u or not
You can paste this into a playground:
extension String {
func array() -> [String] {
return self.componentsSeparatedByString(" ")
}
func abbreviate() -> String {
var output = ""
let array = self.array()
for word in array {
let index = word.startIndex.advancedBy(0)
let str = String(word[index])
if str.lowercaseString != str {
output += str
}
}
return output
}
}
let courseName = "Bachelor of Tourism Administration(B.T.A)".abbreviate()
print(courseName) // prints BTA
A clean approach would be:
extension Character
{
public func isUpper() -> Bool
{
let characterString = String(self)
return (characterString == characterString.uppercaseString) && (characterString != characterString.lowercaseString)
}
}
let courseName = "Bachelor of Tourism Administration(B.T.A)"
let upperCaseCourseName = courseName
let extrctCourseName = upperCaseCourseName.componentsSeparatedByString(" ").reduce("") {
if($0.1.characters.first!.isUpper()) {
return $0.0 + String($0.1.characters.first!)
}else {
return $0.0
}
}
I import a phone-number from "Contacts" and save in NSString.
this string contains white-space and I try to delete them using the method:
numero = numero.stringByReplacingOccurrencesOfString(" ", withString: "")
this method doesn't work.
func sostituisci( stringa: NSString! ) -> NSString
{
var numero: NSString = ""
NSLog(stringa)
numero = ((stringa as String).stringByReplacingOccurrencesOfString(" ", withString: "") as NSString)
NSLog(numero)
return numero
}
the output unchanged
log
2014-11-05 17:54:50.734 HappyRicarica[33438:3119446] (327) 124-3503
2014-11-05 17:54:50.737 HappyRicarica[33438:3119446] (327) 124-3503
I suspect that the space character in your string is not really a space. Try adding this after NSLog(string) to see what the unicode scalar values are for the characters in your string:
for uni in (stringa as String).unicodeScalars {
println("\(uni) = \(uni.value)")
}
The expected output for "(327) 124-3503" is:
( = 40
3 = 51
2 = 50
7 = 55
) = 41
= 32
1 = 49
2 = 50
4 = 52
- = 45
3 = 51
5 = 53
0 = 48
3 = 51
From your comment, your space has value 160 instead of 32. You could remove that with:
numero = stringa.stringByReplacingOccurrencesOfString(String(Character(UnicodeScalar(160))), withString: "")
update: Xcode 7.2 • Swift 2.1.1
extension String {
var numbersOnly: String {
return componentsSeparatedByCharactersInSet(NSCharacterSet(charactersInString: "1234567890")
.invertedSet)
.joinWithSeparator("")
}
var numbersExempt: String {
return componentsSeparatedByCharactersInSet(NSCharacterSet(charactersInString: "1234567890"))
.joinWithSeparator("")
}
var charactersOnly: String {
return componentsSeparatedByCharactersInSet(NSCharacterSet(charactersInString: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ").invertedSet).joinWithSeparator("")
}
var charactersExempt: String {
return componentsSeparatedByCharactersInSet(NSCharacterSet(charactersInString: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")).joinWithSeparator("")
}
func keep(keepIt: String) -> String {
return componentsSeparatedByCharactersInSet(NSCharacterSet(charactersInString: keepIt).invertedSet).joinWithSeparator("")
}
func exclude(excludeIt: String) -> String {
return componentsSeparatedByCharactersInSet(NSCharacterSet(charactersInString: excludeIt)).joinWithSeparator("")
}
}
let phoneNumber = "+1 (555) 555 - 5555".numbersOnly
print(phoneNumber) // "15555555555"
let excludePlusMinus = "+1 (555) 555-5555".exclude("+-") // "1 (555) 5555555"
let keepWhatever = "+1 (555) 555-5555".keep("()-+") // "+()-"
you can also use your function to subtract only spaces with some adjustments. Try like this:
func sostituisci(stringa: String) -> String {
return stringa.stringByReplacingOccurrencesOfString(" ", withString: "")
}
sostituisci("1 234 567 8901") // "12345678901"
or like an extension:
extension String {
var sostituisci: String {
return stringByReplacingOccurrencesOfString(" ", withString: "")
}
}
let phoneNumber2 = "1 234 567 8901".sostituisci
NSString is not String, so you should use:
numero = ((numero as String).stringByReplacingOccurrencesOfString(" ", withString: "") as NSString)
did you try :
let str = "XX XX XX XX XX"
let separated = str.componentsSeparatedByCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet())
let finalStr = "".join(separated)