Calculate count of years - swift

I have a String data that stores date. Example:
let dates = ["1-Jan-2015", "1-Feb-2015", "20-Mar-2014", "15-Apr-2014", "12-May-2013", "23-Jun-2012"]
I need to do a count of how many times did that year occurs, and then store it. So what I require would be something like that
let years = ["2015" : 2, "2014" : 2, "2013" : 1, "2012" : 1]
I would like to avoid hard coding it for future growth. Meaning say if I have Year 2020, I cannot be hard coding all the years to store these values.
So the problem here is I've got no idea how should I do this on the overview. In addition, how do I convert the String format to date?

let dates = ["1-Jan-2015", "1-Feb-2015", "20-Mar-2014", "15-Apr-2014", "12-May-2013", "23-Jun-2012"]
// map the last 4 characters of your string
let years = dates.map{String($0.characters.suffix(4))}
// create a dictionary to store tue frequency
var frequencies:[String:Int] = [:]
// loop through the years
for year in years {
// increase it
frequencies[year] = (frequencies[year] ?? 0) + 1
}
let sortedFrequencies = frequencies.sort{ $0.0 < $1.0 }
print(sortedFrequencies) // [("2012", 1), ("2013", 1), ("2014", 2), ("2015", 2)]\n"

I have created the following solution. Use the following function and it will give you the following result in the screenshot
/// This function will return the array as per your requirement
func getyears(dates: NSArray) -> NSArray
{
var year = [String]()
let newdates = dates
var countvalue = 0
for i in dates {
let x = i.stringByReplacingOccurrencesOfString("-", withString: "")
let startIndex = x.endIndex.advancedBy(-4)
let lastFourDigitsOfdate = x.substringFromIndex(startIndex)
for xm in newdates
{
let xy = xm.stringByReplacingOccurrencesOfString("-", withString: "")
let startIndex1 = xy.endIndex.advancedBy(-4)
let lastFourDigitsOfdate1 = xy.substringFromIndex(startIndex1)
if lastFourDigitsOfdate == lastFourDigitsOfdate1
{
countvalue = countvalue + 1
}
}
year.append("\(lastFourDigitsOfdate) : \(countvalue)")
countvalue = 0
}
return getUniqArrayData(year)
}
// This function will be required to get an unque array
func getUniqArrayData<S: SequenceType, E: Hashable where E==S.Generator.Element>(source: S) -> [E] {
var seen: [E:Bool] = [:]
return source.filter { seen.updateValue(true, forKey: $0) == nil }
}
let dates = ["1-Jan-2015", "1-Feb-2015", "20-Mar-2014", "15-Apr-2014", "12-May-2013", "23-Jun-2012"]
//Calling the above function
getyears(dates)
Hope this will be helpful.

Related

How to create 15 digit length random string in Swift? [duplicate]

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

How to masking textfield in Swift 4? [duplicate]

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)

Split string to arrays with maximum variables in each array

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

Sum the Values of Nested Custom Objects in Swift

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.

More Functional / Swift way to convert time to minutes

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.