This question already has answers here:
Random Password Generator Swift 3?
(3 answers)
Closed 2 years ago.
In Javascript, Node.js I can generate with https://www.npmjs.com/package/uuid package a 15 digit length "random" string. Is it possible it with Swift?
Like this: 802128100247740
const uuidv4 = require("uuid/v4");
tempUserUuid = uuidv4();
Swift 5.0 introduced major improvements in dealing with random values and elements. The following code will help you
func randomString(length: Int) -> String {
let letters = "0123456789"
return String((0..<length).map{ _ in letters.randomElement()! })
}
label.text = randomString(length: 15)
The other answers generate a random number multiple times, but you only need to do it once.
import Foundation
extension String {
/// A leading-zero-padded padded random number.
/// - Returns: nil if digitCount is too big for `UInt` (You get 19 or fewer!)
static func randomNumber(digitCount: Int) -> Self? {
let digitCountDouble = Double(digitCount)
guard digitCountDouble < log10( Double(UInt.max) ) else {
return nil
}
let formatter = NumberFormatter()
formatter.minimumIntegerDigits = digitCount
let upperBound = pow(10, digitCountDouble)
return formatter.string(
for: UInt.random( in: 0..<UInt(upperBound) )
)!
}
}
func randomString(length: Int) -> String {
return (0..<length).map { _ in String(Int.random(in: 0...9)) }.joined()
}
func randomStringBuffer(length: Int) -> String {
var buffer = ""
(0..<length).forEach { _ in buffer += String(Int.random(in: 0...9)) }
return buffer
}
print(randomString(length: 15))
print(randomStringBuffer(length: 15))
first is compact, but second is more efficient, but in this situation (generating only 15 digits string) it doesn't matter, I think
UPD
I made a test, and it says that I was wrong. Seems first approach, with joined() is better
let a = Date().timeIntervalSince1970
print(a)
let g = randomString(length: 10000)
let b = Date().timeIntervalSince1970
print(b)
print(b - a)
let c = Date().timeIntervalSince1970
print(c)
let f = randomStringBuffer(length: 10000)
let d = Date().timeIntervalSince1970
print(d)
print(d - c)
1583933185.788064
1583933185.9271421
0.13907814025878906 // joined() version
1583933185.927207
1583933186.2418242
0.3146171569824219 // buffer version
UPD 2
Also made a public gist also with #deep-kakkar function.
As I can see, "joined()" method made it most efficient
This question already has answers here:
How to masking the last number in Swift?
(2 answers)
Closed 3 years ago.
I want masking email in textfield.text, but I only get the value in the middle. I want to get the value from the middle to # gmail.com like example below.
ex:
let var = 12345678#gmail.com
output = ****5678#gmail.com
let var = 123456789#gmail.com
output = ****56789#gmail.com
let email = "123456789#gmail.com"
let components = email.components(separatedBy: "#")
let result = hideMidChars(components.first!) + "#" + components.last!
print(result)
output I get: ****5****#gmail.com
my expectations: ****56789#gmail.com
try extending the string protocol and declare a variable which returns an .init(repeating: ,count):
extension StringProtocol {
var masked: String {
return String(repeating: "•", count: Swift.max(0, count - count/2)) + suffix(count/2)
}
}
usage as follows:
let email = "123456789#gmail.com"
print(email.masked) //"••••••••••gmail.com"
if you want a part of the email showing just manipulate the suffix(count - 3) as follows:
return String(repeating: "•", count: Swift.max(0, count - count/2)) + suffix(count/2)
func hide(email: String) -> String {
let parts = email.split(separator: "#")
if parts.count < 2 {
return email
}
let name = parts[0]
let appendix = parts[1]
let lenght = name.count
if lenght == 1 {
return "*#\(appendix)"
}
let semiLenght = lenght / 2
var suffixSemiLenght = semiLenght
if (lenght % 2 == 1) {
suffixSemiLenght += 1
}
let prefix = String(repeating: "*", count: semiLenght)
let lastPart = String(name.suffix(suffixSemiLenght))
let result = "\(prefix)\(lastPart)#\(appendix)"
return result
}
let email = "123456789#gmail.com"
let result = hide(email: email)
print(result)
I have a string of numbers (each number is separated by ,) that looks like this:
"12,3,5,75,584,364,57,88,94,4,79,333,7465,867,56,6,748,546,573,466"
I want to split the string to an array of strings, that each element is a string that has maximum 10 number in it.
For the example I've added I want to achieve something like this:
stringsArray:
Element 0: "12,3,5,75,584,364,57,88,94,4"
Element 1: "79,333,7465,867,56,6,748,546,573,466"
And so on...
I've been thinking a lot about a way to do this with Swift, but couldn't find anything...
Does anybody has an idea?
Thank you!
Step 1 - get fully separated array:
let numbers = "12,3,5".components(separatedBy: ",")
Step 2 - chunk your result to parts with ext:
extension Array {
func chunked(by chunkSize: Int) -> [[Element]] {
return stride(from: 0, to: self.count, by: chunkSize).map {
Array(self[$0..<Swift.min($0 + chunkSize, self.count)])
}
}
}
let chunkedNumbers = numbers.chunked(by: 10)
Step 3:
let stringsArray = chunkedNumbers.map { $0.joined(separator: ",") }
Result: ["12,3,5,75,584,364,57,88,94,4", "79,333,7465,867,56,6,748,546,573,466"]
Link to gist playground.
I would look at the position of 10th comma in your original string, get the prefix up to this position, remove this prefix and repeat until remaining string is empty.
This is a bit brute force, but works.
I first add extension to String for convenience.
extension String {
func startIndexesOf(_ string: String) -> [Int] {
var result: [Int] = []
var start = startIndex
while let range = range(of: string, options: .literal, range: start..<endIndex) {
result.append(range.lowerBound.encodedOffset)
start = range.upperBound
}
return result
}
subscript (r: Range<Int>) -> String {
let start = index(self.startIndex, offsetBy: r.lowerBound)
let end = self.index(self.startIndex, offsetBy: r.upperBound)
return String(self[Range(start ..< end)])
}
}
let test = "12,3,5,75,584,364,57,88,94,4,79,333,7465,867,56,6,748,546,573,466,999"
var remaining = test
var arrayOf10 : [String] = []
repeat {
let indexes = remaining.startIndexesOf(",")
if indexes.count < 10 {
arrayOf10.append(remaining) // Just add what remains
break
}
let position = indexes[9]
let endBeginning = remaining.index(test.startIndex, offsetBy: position) // Beginning of what remain to parse
let beginningSubstring = remaining[remaining.startIndex ..< endBeginning]
let beginningText = String(beginningSubstring)
arrayOf10.append(beginningText)
let startNext = remaining.index(test.startIndex, offsetBy: position+1) // What will remain to parse after taking out the beginning
let remainingSubString = remaining[startNext ..< remaining.endIndex]
remaining = String(remainingSubString)
} while remaining.count > 0
for (c, s) in arrayOf10.enumerated() { print("Element", c, ": ", s)}
This will print as desired
Element 0 : 12,3,5,75,584,364,57,88,94,4
Element 1 : 79,333,7465,867,56,6,748,546,573,466
Element 2 : 999
I am trying to add up the values found within some custom objects in Swift. I have the follow code working in a Playground:
//Custom Object
class EntryPack: NSObject{
var date: String!
var duration: Double!
var customValues:[CustomValuePack] = [] //Nested array of objects declared below
}
//Another object to nest in the above
class CustomValuePack: NSObject{
var name:String!
var value: Double!
}
//Instantiate nested objects
let cv1 = CustomValuePack()
cv1.name = "Day"
cv1.value = 1.2
let cv2 = CustomValuePack()
cv2.name = "Day"
cv2.value = 1.3
let cv3 = CustomValuePack()
cv2.name = "Night"
cv2.value = 2.2
//Instantiate parent objects
let entry1 = EntryPack()
entry1.date = "bingo"
entry1.duration = 1.1
entry1.customValues = [cv1, cv2]
let entry2 = EntryPack()
entry2.date = "bingo"
entry2.duration = 2.2
let entry3 = EntryPack()
entry3.date = "dang"
entry3.duration = 3.0
//Throw it all together into an array
var package = [entry1, entry2, entry3]
//Predicate for some filtering
let predicate = NSPredicate(format:"date = %#", "bingo")
let results = (package as NSArray).filteredArrayUsingPredicate(predicate) as! [EntryPack]
//Sum all the parent object durations
let sum = results.reduce(0.0) { $0 + $1.duration }
print(sum) // = 3.3
But now I want to add up the value for each CustomValuePack where the name is "Day".
I can't figure out how to do a similar reduce on a nested array of objects. I've tried something like this, but it yields a syntax error:
let sum2 = results.reduce(0.0) { $0.customValues + $1.customValues.value } //Error
How do I sum the value in a nested array of objects? I eventually want to apply an NSPredicate to filter by name = Day as well, but I'm not that far yet.
Given entries defined as follow
let entries = [entry1, entry2, entry3]
you can extract the CustomValues with name == "Day" and sum the value field with this code
let sum = entries
.flatMap { $0.customValues }
.filter { $0.name == "Day" }
.map { $0.value }
.reduce(0, combine: +)
Improvements
You are using Swift but for some reason there still is a lot of Objective-C into your code.
This is how i would refactor your code
struct EntryPack {
let date: String
let duration: Double
let customValues:[CustomValuePack]
}
struct CustomValuePack {
let name:String
let value: Double
}
let cv1 = CustomValuePack(name: "Day", value: 1.2)
let cv2 = CustomValuePack(name: "Day", value: 1.3)
let cv3 = CustomValuePack(name: "Night", value: 2.2)
let entry1 = EntryPack(date: "bingo", duration: 1.1, customValues: [cv1, cv2])
let entry2 = EntryPack(date: "bingo", duration: 2.2, customValues: [])
let entry3 = EntryPack(date: "dang", duration: 3.0, customValues: [])
Please note that now EntryPack and CustomValuePack are structs which are value types.
I'm trying to convert a length of time in "Hours:Minutes" to "minutes". Time is given as a String, and I want to return a Double of minutes.
Currently I'm using the following function:
func convertMinHoursToDouble(length: String) -> Double {
var hours = 0.0
var minutes = 0.0
let lengthCleaned = length.stringByReplacingOccurrencesOfString(":", withString: "")
var count = 0
for char in lengthCleaned.characters {
if count == 0 {
hours = Double("\(char)")! * 60
} else if count == 1 {
minutes = Double("\(char)")! * 10
} else if count == 2 {
minutes = Double("\(char)")! + minutes
}
++count
}
return hours+minutes
}
let time = "2:16"
let convertedTime = convertMinHoursToDouble(time)
print(convertedTime) // prints 136.0
This works, however I'm trying to do this in a more functional / Swift way. How can it be done with the reduce function. This is the closest I can get to the solution.
let convertedTime = time.characters.reduce(0) { (dub, char) in dub + Double(String(char))! }
The pure Swift way would be :
let time = "02:16"
let converted = time.characters.split(":")
.flatMap { Int(String($0)) }
.reduce(0) { $0 * 60 + $1 }
print(converted) //"136\n"
Functional solution:
func convertMinHoursToDouble(time: String) -> Int {
let timeComps = (time as NSString).componentsSeparatedByString(":") as [NSString]
return timeComps.reduce(0) { acc, item in
acc * 60 + item.integerValue
}
}
let time = "02:15"
let convertedTime = convertMinHoursToDouble(time)
print(convertedTime)
You split the string into components, and reduce on that array. This works like a charm also for strings like "03:24:34" for which it computes the time in seconds.
You can add additional validation logic if you want to deal with malformed strings: no ":", more than one ":", invalid minutes value (e.g. 78), etc.