Sorting an array of month chronologicaly in swift - swift

Here is my array that i wan't to sort:
["Septembre", "Novembre", "Août", "Mars", "Décembre", "Octobre", "Juillet", "Avril", "Juin", "Février", "Janvier", "Mai"]
How can i sort this array?

You will need to make a Dictionary with key as the month name in String and value in int corresponding to the order in which the months come in the year. Then using this library http://www.dollarswift.org/#keys-keys you can
let monthsDict = ["feb": 1, "mar": 2, "jan": 0]
var months = $.keys(monthsDict)
months.sort {
monthsDict[$0] < monthsDict[$1]
}
If you want it reversed you can just flip the equality operator
months.sort {
monthsDict[$0] > monthsDict[$1]
}

Related

Sort an array of objects, following the order of another array that has a value of the objects of the first array

How could I sort an array of objects taking into account another array?
Example:
let sortArray = [90, 1000, 4520]
let notSortArrayObject = [ { id: 4520, value: 4 }, {id: 1000, value: 2}, {id: 90, value:10} ]
So I want an array equal notSortArrayObject but sortered by sortArray value
let sortArrayObject =[{id: 90, value:10} , {id: 1000, value: 2}, { id: 4520, value: 4 }]
Here's an approach. For each number in the "guide" array, find a match in the not sorted and add it to the sortedObjectArray. Note that this ignores any values which don't have matches in the other array.
var sortedObjectArray: [Object] = [] // whatever your object type is
for num in sortArray {
if let match = notSortArrayObject.first(where: {$0.id == num}) {
sortedObjectArray.append(match)
}
}
If you just want to sort by the id in ascending order, it's much simpler:
let sorted = notSortArrayObject.sorted(by: {$0.id < $1.id})
I noticed that sortArray is already sorted - you could just sort notSortArrayObject by id, but I assume that's not the point of what you're looking for.
So, let's say that sortArray = [1000, 4520, 90] (random order), to make things more interesting.
You can iterate through sortArray and append the values that match the id to sortArrayObject.
// To store the result
var sortArrayObject = [MyObject]()
// Iterate through the elements that contain the required sequence
sortArray.forEach { element in
// Add only the objects (only one) that match the element
sortArrayObject += notSortArrayObject.filter { $0.id == element }
}

Collect values from list of objects into dictionary in swift5

I have a list of Employee objects (an array of Employee objects) that consists of several properties and I would like to get a dictionary (similar to a hash map in Java) with the month and the hoursWorked. I looked at other posts but I only found examples where I can collect one of the items using map -> .map(\.month) but not two values
struct Employee {
var id: UUID
var name: String
var month: Date
var hoursWorked: Double
var ...
var ...
}
Sample Data - an array of Employee objects:
{month: 2019-01, hoursWorked: 256, id:..., name...}
{month: 2019-02, hoursWorked: 200, id:..., name...}
{month: 2019-03, hoursWorked: 300, id:..., name...}
{month: 2019-04, hoursWorked: 150, id:..., name...}
{month: 2019-05, hoursWorked: 100, id:..., name...}
{month: 2019-06, hoursWorked: 220, id:..., name...}
.
.
.
Expected Result:
a Dictionary consisting of Month and hoursWorked:
[2019-01: 256.0]
[2019-02: 200.0]
[2019-03: 300.0]
[2019-04: 150.0]
[2019-05: 100.0]
[2019-06: 220.0]
...
One way to accomplish this is to loop through the Employee array and collect the month and the hoursWorked into a Swift Dictionary (Date:Double in Swift) (similar to hash map in Java). I was wondering if there is a better/simpler (perhaps, faster) way to accomplish this in Swift.
Thanks!
Create a dictionary by grouping the array per month and then use mapValues and reduce to calculate the sum per month
let stats = Dictionary(grouping: employees, by: \.month)
.mapValues { $0.reduce(into: 0, { $0 += $1.hoursWorked }) }
Edit: here is an alternative solution based on the comment by #Alexander-ReinstateMonica
extension Sequence {
func sum() -> Element where Element: AdditiveArithmetic {
self.reduce(.zero, +)
}
}
let stats = Dictionary(grouping: employees, by: \.month)
.mapValues { $0.lazy.map(\.hoursWorked).sum() }
Edit 2: tuples instead of dictionary
Both solution above are quite simple to convert to an array of tuples, here is the second solution as tuples sorted on month
let stats = Dictionary(grouping: employees, by: \.month)
.map( { ($0.key, $0.value.lazy.map(\.hoursWorked).sum()) })
.sorted(by: {$0.0 < $1.0})
You can use reduce(into:_:) function for this:
let dictionary = emplyees.reduce(into: [Date: Double]()) { (result, employee) in
result[employee.month, default: 0] += employee.hoursWorked
}
This will create a dictionary [Date: Double] that have as key the month and as value the sum of hours worked for that month.
Update: To return an array of tuple instead, you need a little bit extra work:
let dictionary = emplyees.reduce(into: [(month: Date, hours: Double)]()) { (result, employee) in
if let index = result.firstIndex(where: { $0.month == employee.month }) {
result[index].hours += employee.hoursWorked
} else {
result.append((month: employee.month, hours: employee.hoursWorked))
}
}

How to sort a dictionary based on keys and sort its respective values in Swift

I have a dictionary with string as key and an array of int as values , i need to sort the dictionary based on keys and the array value should also be sorted.
var dicNumArray : Dictionary<String , [Int]> = ["q":[4,3,2,1,5],"a":[2,3,4,5,5],"s":[123,123,132,43,4],"t":[0,88,66,542,321]]
The result i need is the dictionary itself where it is sorted by keys and respective values are also sorted.
You can apply sorted to each the value of the key-value pair of a dictionary using mapValues
And then, you can just use sorted with a predicate comparing the keys of the dictionary.
let result = dicNumArray.mapValues { $0.sorted() }
.sorted { $0.key < $1.key }
This will return an array of key-value pair tuples.
Since, dictionaries can't be trusted with order, working with an array of the key-value pairs is the next best approach.
We can use .key and .value to get the respective values.
result.first?.key // First key
result.first?.value // First value
Dictionaries don't have an order in Swift. Having said that, you can do something like this
var dicNumArray : Dictionary<String , [Int]> = ["q":[4,3,2,1,5],"a":[2,3,4,5,5],"s":[123,123,132,43,4],"t":[0,88,66,542,321]]
func sortData() {
for (key, value) in dicNumArray {
dicNumArray[key] = value.sorted(by: { $0 < $1 })
}
}
sortData()
This will sort array for each key. Once that's done, you can do something like
let keys = Array(dicNumArray.keys).sorted(by: { $0 < $1 })
This will give you a sorted array of dictionary keys. You can test it as follows
TEST
for key in keys {
print("\(key): \(dicNumArray[key]!)")
}
OUTPUT
a: [2, 3, 4, 5, 5]
q: [1, 2, 3, 4, 5]
s: [4, 43, 123, 123, 132]
t: [0, 66, 88, 321, 542]
you can apply sort key , then map to sorted by array
let sortedKeysAndValues = dicNumArray.sorted(by: {$0.0 < $1.0}).map { [$0.key:$0.value.sorted(by: <)]}.flatMap({$0})

Find sum of an attribute for each object in an array

What is the best way to find the sum of an attribute for an array of objects. Lets say I have an array of type item. item has attribute price. How can i sum each item.price inside an array of items. thanks!
One way is to use the reduce function on the array of items:
struct Item {
var price: Double
}
let items = [Item(price: 2), Item(price: 3), Item(price: 7)]
let total = items.reduce(0, { $0 + $1.price })
print(total) // 12
You can also use a more traditional loop as well.
var total = 0
for item in items {
total += item.price
}

Swift turn an Array into a grouped Dictionary [duplicate]

This question already has answers here:
How to group by the elements of an array in Swift
(16 answers)
Closed 7 years ago.
I have an Array of Transaction objects
var returnedArray: Array<Transaction> = [a, b, c, d]
One of the properties of Transaction is an NSDate. I want to convert my Array into a Dictionary
var transactions: [NSDate: Array<Transaction>]
Where all the transactions on that date will be in an Array that has the date as the key.
I know how to loop through each element of my array and manually assign it to the right key but i'm wondering if there is an elegant function to do that.
The dates form a set
var setOfDates = Set (returnedArray.map (transDate))
You can create a dictionary from a sequence of pairs:
var result = Dictionary (dictionaryLiteral: setOfDates.map {
(date:NSDate) in
return (date, returnedArray.filter { date == $0.transDate })
}
You could define this as an array extension. Something like:
extension Array {
func splitBy<Key:Hashable> (keyMaker: (T) -> Key) -> Dictionary<Key, T> {
let theSet = Set (self.map (keyMaker))
return Dictionary (dictionaryLiteral: theSet.map { (key:Key) in
return (key, self.filter { key == keyMaker($0) })
})
}
}
I'm typing this in the dark as you haven't provided a lot of details in your question. You need to improvise:
// Get the list of distinct dates
var dates = [NSDate]()
for tran in returnedArray {
if !dates.contains(tran.transactionDate) {
dates.append(tran.transactionDate)
}
}
// Now group according to date
var result = [NSDate : Array<Transaction>]()
for d in dates {
let transactions = returnedArray.filter { $0.transactionDate == d }
result[d] = transactions
}