Swift Create new array based on two arrays under certain conditions - swift

I need some help in order to establish a new array in Swift 2.0.
I have two arrays, one contains dates and the other payments at this date.
let year = [February 2016, March 2016, June 2017, October 2017, January 2018, April 2019] // Data at which a payment is initiated
let payment = ["1000","2000,"3000","1000","2000,"3000"] // payment amount at date in array year
I'd like to create two new arrays in Swift code based on this.
The final result should look like this:
let yearSum [2016, 2017, 2018,2019] // only full year
let paymentSum ["3000","4000","2000","3000"] // sum of all payment in the year
The array "yearSum" should contain only the full year number, while "paymentSum" should contain the sum of all payments in the year.
Has anybody an advice how I can code this?
Many thanks

The input
First of all let's assign good names to the input constants
let monthStrings = ["February 2016", "March 2016", "June 2017", "October 2017", "January 2018", "April 2019"]
let paymentStrings = ["1000", "2000", "3000", "1000", "2000", "3000"]
What can go wrong
We are working with strings as input, so many things could go wrong during the parsing of a Date or of an Int. For clarity lets define the following enum
enum Error: ErrorType {
case InputParamsHaveDifferentSizes(Int, Int)
case FirstParamHasInvalidDate
case SecondParamHasInvalidInt
}
The function
func groupData(monthStrings: [String], paymentsStrings:[String]) throws -> [Int:Int] {
// make sure both arrays have the same size
guard monthStrings.count == paymentStrings.count
else { throw Error.InputParamsHaveDifferentSizes(monthStrings.count, paymentStrings.count) }
let formatter = NSDateFormatter()
formatter.dateFormat = "MMM yyyy"
// creates dates: an array of NSDate representing monthStrings
// if dates has a different size of monthsString then throws and error
guard
case let dates = (monthStrings.flatMap { formatter.dateFromString($0) })
where dates.count == monthStrings.count
else { throw Error.FirstParamHasInvalidDate }
// creates payments: an array of Int representing paymentsStrings
// if payments has a different size of paymentsStrings then throws and error
guard
case let payments = (paymentStrings.flatMap { Int($0) })
where payments.count == paymentStrings.count
else { throw Error.SecondParamHasInvalidInt }
// put togheter dates and payments and group the results by year
return zip(dates, payments).reduce([Int:Int]()) { (var result, elm) -> [Int:Int] in
let year = NSCalendar.currentCalendar().components([NSCalendarUnit.Year], fromDate: elm.0).year
result[year] = elm.1 + (result[year] ?? 0)
return result
}
}
Usage
let res = try groupData(monthStrings, paymentsStrings: paymentStrings)
print(res) // [2018: 2000, 2017: 4000, 2016: 3000, 2019: 3000]
Update
In the comment below you say you need to access the keys by index and you need them sorted so
let sortedKeys = res.keys.sort()
func value(index:Int) -> String? {
let key = sortedKeys[index]
let value = res[key]
return value
}

Well, due to lack of response I'll just assume years is an array of strings:
// Data
let years = ["February 2016", "March 2016", "June 2017", "October 2017", "January 2018", "April 2019"]
let payed = ["1000", "2000", "3000", "1000", "2000", "3000"]
// Preparation
var result: [String: Double] = [:]
let digits = NSCharacterSet.decimalDigitCharacterSet().invertedSet
// Results
let yearOnly = years.flatMap { $0.componentsSeparatedByCharactersInSet(digits).filter { !$0.isEmpty } }
zip(yearOnly, payed).forEach { result[$0.0] = (result[$0.0] ?? 0) + (Double($0.1) ?? 0) }
Note that yearOnly is created by filtering out all non-digits, so if you'll have days inside of years then this won't work and you'll have to use another method to filter out the years.
// Output
["2018": 2000.0, "2019": 3000.0, "2016": 3000.0, "2017": 4000.0]

Related

What data does the compiler expects with "Date" type?

I have following code on SWIFT with "Date()"
func howLongYouLive(date: Date) -> Double {
let currentDate = Date()
return currentDate.timeIntervalSince(date)
}
howLongYouLive(date: **?**)
When you call the function, what data you need to input? Screen attached.
enter image description here
I've tried to input any type of date I could think of but doesn't seem to work.
I suggest you read the fundamental basics of Swift, here: https://docs.swift.org/swift-book/LanguageGuide/TheBasics.html
When you call the function, what data you need to input?, well you use a Date type.
Try this example code:
// calculate howLongYouLive in seconds
func howLongYouLive(date: Date) -> Double {
// some other calculations here I presume
return Date().distance(to: date)
}
// a date, 99 years in the future from today
let futureDate = Calendar.current.date(byAdding: .year, value: 99, to: Date())!
print("\n---> futureDate: \(futureDate) ")
// the calculated howLongYouLive in seconds
let lifeExpectency = howLongYouLive(date: futureDate) // <-- here
print("\n---> calculated howLongYouLive: \(lifeExpectency) seconds")
// the calculated howLongYouLive as a Date
let lifeDate = Date().addingTimeInterval(lifeExpectency)
print("\n---> howLongYouLive as date: \(lifeDate) ")
// the calculated howLongYouLive as a number of years
let years = Calendar.current.dateComponents([.year], from: Date(), to: lifeDate).year
print("\n---> howLongYouLive: \(years) years \n")

How to use date for x-Axis in SwiftChart?

I am trying to draw line chart in my project using SwiftChart pod. I am getting response from API as ["activity_count":0 , "date": "2020-10-31"] but graph works on double values so I don't know How I draw the chart.
import UIKit
import SwiftChart
class AnalyticsViewController: UIViewController {
#IBOutlet weak var graphContainer: UIView!
var graphData = [(x:0, y: 0.0)]
var graphpResponseData = [[String: Any]]()
override func viewDidLoad() {
super.viewDidLoad()
graphpResponseData.append(["activity_count":0 , "date": "2020-10-31"])
graphpResponseData.append(["activity_count":2 , "date": "2020-10-30"])
graphpResponseData.append(["activity_count":1 , "date": "2020-10-29"])
graphpResponseData.append(["activity_count":29 , "date": "2020-10-28"])
graphpResponseData.append(["activity_count":1 , "date": "2020-10-27"])
graphpResponseData.append(["activity_count":0 , "date": "2020-10-26"])
graphpResponseData.append(["activity_count":0 , "date": "2020-10-25"])
graphpResponseData.append(["activity_count":0 , "date": "2020-10-24"])
graphpResponseData.append(["activity_count":0 , "date": "2020-10-23"])
graphpResponseData.append(["activity_count":0 , "date": "2020-10-22"])
???? unable to proceed
//let chart = Chart(frame: CGRect(x: 0, y: 0, width: 300, height: 100))
//let series = ChartSeries(data: graphData)
//chart.add(series)
//chart.xLabels = [1, 2, 3, 4]
//chart.xLabelsFormatter = { String(Int(round($1))) + "Oct" }
//graphContainer.addSubview(chart)
}
}
desired output
I just had to solve this issue. I was using the day of the month of each date in memory for the x-axis, but it turned out that this would cause the graph to act unexpectedly and show randomly plotted points when the month changed.
I also tried using timestamps for x (of type Double, which would be accepted by the ChartSeries's data initializer, but this failed too.
The solution I found was to use an increment of one for each point on the x-axis, and using an array of [Date] whose indices corresponded with the order of the datapoints in your chart's data property (for the sake of example, call this correspondingDates). Inside of chart.xLabelsFormatter, I'd access this array using correspondingDates[value]. From there, I used two dateFormatter extensions to access the values.
Here's the code:
chart.xLabelsFormatter = { (index,value) in
//Format for label
let date = correspondingDates[Int(value)]
let dayAbbr = DateFormatter.getDay(date: date)
let monthAbbr = DateFormatter.getMonth(date: date)
return monthAbbr + "-" + dayAbbr
Extensions for DateFormatter:
extension DateFormatter{
/// Used specifically to get the Month Abrreviation (ie Feb), but can be used for formatting in other ways.
/// - Parameter format: Can be 'LLL' for the month abbreviated to 3 letters, or something like 'D/MM/Y'
/// - Returns: Returns the String version of the date, with precision at any level
static func getMonth(format:String = "LLL",date: Date = Date())->String{
let formatter = DateFormatter()
let today = date // formatter.da(from: Calendar.current.dateComponents([.day], from: Date()))
formatter.dateFormat = format
let string = formatter.string(from: today)
return string
}
/// Used specifically to get the Day Abrreviation (ie 05 or 27), but can be used for formatting in other ways.
/// - Parameters:
/// - format: Can be 'LLL' for the month abbreviated to 3 letters, or something like 'D/MM/Y'
/// - date: optionally pass in your own date
/// - Returns: Returns the String version of the day, with precision at any level
static func getDay(format:String = "dd",date: Date = Date())->String{
let formatter = DateFormatter()
let today = date // formatter.da(from: Calendar.current.dateComponents([.day], from: Date()))
formatter.dateFormat = format
let string = formatter.string(from: today)
return string
}
}

filter array by timestamps if the days are the same days

i have an array and inside of it List type of objects
struct List:Decodable {
let dt: Int //1593183600
let dateTime: String //2020-06-26 15:00:00
}
i want to filter the array if object dt is already in array.
examle:
dt: 1593183600,//2020-06-26 21:00:00
dt: 1593216000// 2020-06-27 00:00:00
dt: 1593226800//2020-06-27 03:00:00
the array should not contain 2020-06-27 . it does not matter which one should be remove
my main purplose is writing the weekdays according to time interval values. any diffirent idea would be great
according to your link i tried it like that
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd"
//var filteredDays: [List] = []
let unique = data.unique{formatter.date(from: $0.dateTime)}
print(data.count)//40
print(unique.count) //1
it should return 5 days but return one
Use Dictionary(grouping:by) to group the elements by date and then select the first value from each group
let list = Dictionary(grouping: array,
by: { Calendar.current.startOfDay(for: Date(timeIntervalSince1970: TimeInterval($0.dt))) })
.compactMap { $0.value.first }
You can remove all objects containing the specified date by filtering a list like this:
struct Decodable {
let dt: Int //1593183600
let dateTime: String //2020-06-26 15:00:00
}
var list: [Decodable] = []
list.append(Decodable(
dt: 1,
dateTime: "2020-06-26 15:00:00"))
list.append(Decodable(
dt: 1,
dateTime: "2020-06-27 15:00:00"))
let filteredList = list.filter { !$0.dateTime.contains("2020-06-27") }
print(filteredList)

How to determine the next days of the week if I know the current day?

I created a weather application and for it I used API. (I used OpenWeatherMap and API as "Call 5 day / 3 hour forecast data")
I got all of the information that I need.
Then I got data for all days of the week. For example, Monday temp 22 degrees, Tuesday temp 12 degrees, and etc.
I can get the current day of the week and added to dictionary.
But I can't get new dayName (day of the week). What do I need to do?
I need help getting the names of the next days of the week and their indexes. And then add the key value to the dictionary.
// Indexed all days of the week Sun, Mon, Wed and a t.c
let setWeekDay = IndexSet([1, 2, 3, 4, 5, 6, 7])
//I detected current day of the weak and added to dictionary
var weakDay = [String:Int]()
let calendar = Calendar.current
var weekday = calendar.component(.weekday, from: Date())
let components = DateComponents(weekday: weekday)
var dayName = calendar.nextDate(after: Date(), matching: components, matchingPolicy: .nextTime)?.Days()
weakDay = [dayName!:weekday]
if let nextWeekday = setWeekDay.integerGreaterThan(weekday) {
weekday = nextWeekday
} else {
weekday = setWeekDay.first!
}
...
// I extended date formatter
extension Date{
func Days() -> String{
let dataFormatter = DateFormatter()
dataFormatter.dateStyle = .short
dataFormatter.dateFormat = "EEEE"
return dataFormatter.string(from: self)
}
}
//This is dictionary will be change key and value dynamically.
//I need got dictionary as:
let weakDay = [
"Sunday": 1,
"Monday": 2,
"Tuesday": 3,
"Wednesday": 4,
"Thursday": 5,
"Friday": 6,
"Saturday": 7,
]
Thanks all of the one for help, but i found answer on my question myself!
What i was need to be do:
if let cell = tableView.dequeueReusableCell(withIdentifier: "daysId", for: indexPath) as? DaysTVCell {
var nameWeakAndIndex = [String:Int]()
var dateComponents = DateComponents()
dateComponents.setValue(1, for: .day);
var now = Date()
for index in 0...5 {
let tomorrow = Calendar.current.date(byAdding: dateComponents, to: now)
let detectDayIndex = index
nameWeakAndIndex = [now.Days():detectDayIndex]
now = tomorrow!
daysWeak.sort(by: { (nameWeakAndIndex[$0.dayOfTheWeak] ?? 6) > (nameWeakAndIndex[$1.dayOfTheWeak] ?? 6) })
}
let day = daysWeak[indexPath.row]
cell.configureCell(daysForecast: day)
return cell

Turning an Int into a String

I'm pretty new to swift (and programming altogether). I'm trying to convert an Int into a String. I've tried using switch statements but every time I use them, it never changes to the String (AKA it prints the number 4) An example of what I'm trying to do is as follows:
class Birthday(_ month: Int, _ day:Int, _ year:Int) -> String{
//Here is where I'd like to turn my month into April
Return (month)
}
let example = Birthday()
example(4,15,1988)
If you really just want to get a month name from a month number, you can do the following:
let formatter = DateFormatter()
let monthName = formatter.monthSymbols[month - 1] // Assuming 1 means January
But since you are passing in a month, day, and year, you presumably want to create a Date and then you want to format that Date into a `String.
Create a Date using Calendar and DateComponents.
let date = Calendar.current.date(from: DateComponents(year: year, month: month, day: day))
Then you format the Date into a String using DateFormatter.
let formatter = DateFormatter()
formatter.dateStyle = .long // choose a desired style
formatter.timeStyle = .none
let string = formatter.string(from: date)
You can use a dictionary which maps objects to each other. For example, a months dictionary could look like:
let months: [Int:String] = [1:"January", 2:"February",...]
return months[4] // returns "April"
Simple solution to get you started would be a method that takes an integer and return your month string.
func numberToMonth(number: Int) -> String {
guard number > 0, number < 13 else { return "" }
return DateFormatter().monthSymbols[number-1]
}