I want to sort this kind of Dictionary:
var data = [NSDate: [String: AnyObject]]()
e.g.
data: [2015-08-29 12:08:29 +0000: ["foo": "bar"]],
[2015-08-29 12:05:13 +0000: ["foo": "bar"]],
[2015-08-29 12:11:02 +0000: ["foo": "bar"]]
should then be:
dataSorted: [2015-08-29 12:05:13 +0000: ["foo": "bar"]],
[2015-08-29 12:08:29 +0000: ["foo": "bar"]],
[2015-08-29 12:11:02 +0000: ["foo": "bar"]]
I tried:
let dataSorted = Array(data.keys).sorted(<)
// Could not find an overload for '<' that accepts the supplied arguments
and
let dataSorted = sorted(data) { $0.0 < $1.0 }
// Could not find member '0'
You cannot sort dictionaries as they do not have order, so I use array in my answer instead.
By default NSDate does not conform to Comparable protocol. You have to use compare method:
let results = Array(data).sort({ $0.0.compare($1.0) == .OrderedAscending })
This extension can be used to make NSDate conform to Comparable:
extension NSDate: Comparable {}
public func ==(lhs: NSDate, rhs: NSDate) -> Bool {
return lhs.compare(rhs) == .OrderedSame
}
public func >(lhs: NSDate, rhs: NSDate) -> Bool {
return lhs.compare(rhs) == .OrderedDescending
}
public func <(lhs: NSDate, rhs: NSDate) -> Bool {
return lhs.compare(rhs) == NSComparisonResult.OrderedAscending
}
public func <=(lhs: NSDate, rhs: NSDate) -> Bool {
return lhs < rhs || lhs == rhs
}
public func >=(lhs: NSDate, rhs: NSDate) -> Bool {
return lhs > rhs || lhs == rhs
}
Then you can simply use:
let results = Array(data).sort({ $0.0 < $1.0})
An (at least for me) easier to read solution without $0 stuff would be:
let kartoffel = data.keys.sort { (date, date2) in
return date.compare(date2) == NSComparisonResult.OrderedAscending
}
Related
I have an array of objects like this, but with 16 properties:
class anObject: NSObject {
#objc var number: Int
#objc var name: String
#objc var price: Double
subscript(key: String) -> Any? {
return self.value(forKey: key)
}
}
I am able to sort my array on any property very easily by, for instance:
sortedArray = unsortedArray.sorted(by: { $0.name < $1.name } )
Now I am grouping my array so that I can populate a UITableView with sections and rows. I group it like this:
var groupedArray = Dictionary<String, Array<myObject>>()
for item in myArray {
// Verify each grouping is initialized only once
if groupedArray[item[byProperty] as! String] == nil {
groupedArray[item[byProperty] as! String] = Array<Items>()
}
// Add the item into the correct subarray
groupedArray[item[byProperty] as! String]?.append(item)
}
I can then sort the grouped array by doing this:
return groupedArray.sorted { $0.0 < $1.0 }
And this works great, except that two of my properties are Doubles. When I sort on those two properties, Swift sorts the groups alphabetically:
10.5, 11.5, 12, 1.5, 2.0 . . .
rather than numerically
1.5, 2.0, 10.5, 11.5, 12 . . .
I have managed to pad the Doubles by checking to see if they are too short and inserting a 0 at the front of the String. This works in that they are now sorted in correct order, but eventually I am going to have to strip that leading 0 off the front, and it seems like an ugly solution.
How do I properly sort the grouped array given that the Doubles have to be used as Strings?
When you start casting strings all over the place, you likely need to start changing the design of things. Why not make the dictionary keys some object you've designed instead of a string? This is a sample of what I mean:
struct DoubleKey {
let value: Double
}
extension DoubleKey: Hashable {
var hashValue: Int {
return value.hashValue
}
static func ==(lhs: DoubleKey, rhs: DoubleKey) -> Bool {
return lhs.value == rhs.value
}
}
extension DoubleKey: Comparable {
static func <(lhs: DoubleKey, rhs: DoubleKey) -> Bool {
return lhs.value < rhs.value
}
}
let a = DoubleKey(value: 10.0)
let b = DoubleKey(value: 20.0)
let c = DoubleKey(value: -10.0)
let dictionary: [DoubleKey: String] = [a: "10", b: "20", c: "-10"]
let sortedDictionary = dictionary.sorted { $0.0 < $1.0 }
So instead of [String: Array<myobject>] you have: [DoubleKey: Array<MyObject> or [IntegerKey: Array<MyObject>] or even [StringKey: Array<MyObject>
You could implement many variations of your own specialized key, and write some protocols if you need additional functionality. If you need to store a string in your key, then add a property, or better yet, conform it to a protocol that defines the behavior of what you need and implement it.
Additional Key
struct StringKey {
let value: String
}
extension StringKey: Hashable {
var hashValue: Int {
return value.hashValue
}
static func ==(lhs: StringKey, rhs: StringKey) -> Bool {
return lhs.value == rhs.value
}
}
extension StringKey: Comparable {
static func <(lhs: StringKey, rhs: StringKey) -> Bool {
return lhs.value < rhs.value
}
}
let a = StringKey(value: "a")
let b = StringKey(value: "c")
let c = StringKey(value: "b")
let dictionary: [StringKey: String] = [a: "10", b: "20", c: "-10"]
let sortedDictionary = dictionary.sorted { $0.0 < $1.0 }
Now what?
//EXAMPLE
protocol ViewableString {
func view() -> String
}
extension StringKey: ViewableString {
func view() -> String {
return value
}
}
extension DoubleKey: ViewableString {
func view() -> String {
return String(value)
}
}
let array: [ViewableString] = [a, b, c]
array[0].view()
Program to the protocols!
Hopefully this helps!
Ok. Completely different answer. Again, you are trying to fit many objects of different types in the same container. This feels like a bad idea to me. Maybe you have to. But here is one way using enums:
enum SpecialKey {
case integer(Int)
case double(Double)
case string(String)
func asString() -> String {
switch self {
case let .integer(a):
return String(a)
case let .double(a):
return String(a)
case let .string(a):
return a
}
}
}
extension SpecialKey: Comparable {
static func <(lhs: SpecialKey, rhs: SpecialKey) -> Bool {
switch (lhs, rhs) {
case let (.double(a), .double(b)):
return a < b
case let (.integer(a), .integer(b)):
return a < b
case let (.string(a), .string(b)):
return a < b
default:
return false //Add more cases with different comparisons!
}
}
static func ==(lhs: SpecialKey, rhs: SpecialKey) -> Bool {
switch (lhs, rhs) {
case (.integer(_), .integer(_)),
(.double(_), .double(_)),
(.string(_), .string(_)):
return true
default:
return false
}
}
}
extension SpecialKey: Hashable {
var hashValue: Int {
switch self {
case let .integer(a):
return a.hashValue
case let .double(a):
return a.hashValue
case let .string(a):
return a.hashValue
}
}
}
let a = SpecialKey.integer(10)
let b = SpecialKey.string("something")
let c = SpecialKey.double(10.5)
let dictionary: [SpecialKey: String] = [a: "a", b: "b", c: "c"]
This is probably more like what you're looking for.
I wrote two functions to compare arrays. Now I am struggling how to compare these approaches and find out what is the most suitable.
I would like to know how could I evaluate each function that I wrote.
By equal I understand that all elements of arrays are same.
By evaluate I am asking performance (Time and Complexity) and usage of Swift features.
My structure that is used in arrays:
struct Wallet {
var fund = 0
var name = ""
public static func ==(lhs: Wallet, rhs: Wallet) -> Bool{
return lhs.fund == rhs.fund && lhs.name == rhs.name
}
public static func !=(lhs: Wallet, rhs: Wallet) -> Bool{
return lhs.fund != rhs.fund || lhs.name != rhs.name
}
}
First approach with usage of the zip:
func compare(x: Array<Wallet>, y: Array<Wallet>) -> Bool {
let state = zip(x, y).enumerated().filter() {
$1.0 == $1.1
}.count == x.count
return state
}
Second approach when I sort array and than compare.
func compare(x: Array<Wallet>, y: Array<Wallet>) -> Bool {
if x.count != y.count {
return false
}
let xSorted = x.sorted{$0.fund > $1.fund}
let ySorted = y.sorted{$0.fund > $1.fund}
for (pos, xItem) in xSorted.enumerated() {
let yItem = ySorted[pos]
if xItem != yItem {
return false
}
}
return true
}
Both solutions are not optimal:
Calling enumerated() is unnecessary because the index position is
not used in the closure.
Both solutions do not short-circuit: They compare all array elements
even if a different pair is found.
contains() does short-circuit, so this would be a better solution:
func compare(x: Array<Wallet>, y: Array<Wallet>) -> Bool {
return x.count == y.count &&
!zip(x, y).contains { $0 != $1 }
}
Alternatively:
func compare(x: Array<Wallet>, y: Array<Wallet>) -> Bool {
return x.elementsEqual(y, by: ==)
}
But you get that for free if you make the type Equatable (note
that you don't have to implement !=, that is provided automatically:
struct Wallet: Equatable {
var fund = 0
var name = ""
public static func ==(lhs: Wallet, rhs: Wallet) -> Bool{
return lhs.fund == rhs.fund && lhs.name == rhs.name
}
}
Now you can compare arrays simply with ==, using the existing
func ==<Element>(lhs: Array<Element>, rhs: Array<Element>) -> Bool
where Element : Equatable
operator.
I've been using the sort() function but it mixes up the relative order.
This is how my code looks.
recipes.sort { $0.skill.value <= $1.skill.value }
Swift API says that:
The sorting algorithm is not stable. A nonstable sort may change the
relative order of elements that compare equal.
How can I change this so that the relative order stays the same as before?
The implementation below just work like the sorted method in the standard library, without additional limit.
extension RandomAccessCollection {
/// return a sorted collection
/// this use a stable sort algorithm
///
/// - Parameter areInIncreasingOrder: return nil when two element are equal
/// - Returns: the sorted collection
public func stableSorted(by areInIncreasingOrder: (Element, Element) throws -> Bool) rethrows -> [Element] {
let sorted = try enumerated().sorted { (one, another) -> Bool in
if try areInIncreasingOrder(one.element, another.element) {
return true
} else {
return one.offset < another.offset
}
}
return sorted.map { $0.element }
}
}
A stable sort needs to preserve the original order. So we give every element a weight of order besides its value, the index, then the original sort method will just work, as there will never be 2 equal elements.
I appreciate the elegance of leavez's answer. I adapted it to have the same signature as Sequence.sorted(by:):
extension Sequence {
func stableSorted(
by areInIncreasingOrder: (Element, Element) throws -> Bool)
rethrows -> [Element]
{
return try enumerated()
.sorted { a, b -> Bool in
try areInIncreasingOrder(a.element, b.element) ||
(a.offset < b.offset && !areInIncreasingOrder(b.element, a.element))
}
.map { $0.element }
}
}
let sortedArray = (recipes as NSArray).sortedArray(options: .stable, usingComparator: { (lhs, rhs) -> ComparisonResult in
let lhs = (lhs as! Recipe)
let rhs = (rhs as! Recipe)
if lhs.skill.value == rhs.skill.value {
return ComparisonResult.orderedSame
} else if lhs.skill.value < rhs.skill.value {
return ComparisonResult.orderedAscending
} else {
return ComparisonResult.orderedDescending
}
})
Took from here: https://medium.com/#cocotutch/a-swift-sorting-problem-e0ebfc4e46d4
In Swift 5 sort() uses stable implementation and soon it will become officially guaranted to be stable.
From Swift forums:
...
On the other hand, the actual implementation calls
/// Sorts the elements of this buffer according to `areInIncreasingOrder`,
/// using a stable, adaptive merge sort.
///
/// The adaptive algorithm used is Timsort, modified to perform a straight
/// merge of the elements using a temporary buffer.
#inlinable
public mutating func _stableSortImpl(
by areInIncreasingOrder: (Element, Element) throws -> Bool
) rethrows { ... }
And
If I recall, sort() is currently stable, but it is not yet guaranteed
to be stable (meaning, the fact that it is stable is currently an
implementation detail, and a future version of Swift could ship an
unstable algorithm instead).
I use this wrapper
extension Array where Element: Comparable, Element: AnyObject {
public func stableSorted() -> [Element] {
let array = self as NSArray
let result = array.sortedArray(options: .stable) { (left, right) -> ComparisonResult in
let left = left as! Element
let right = right as! Element
if left < right {
return ComparisonResult.orderedAscending
}
if left > right {
return ComparisonResult.orderedDescending
}
return ComparisonResult.orderedSame
}
return result as! [Element]
}
public func stableReversed() -> [Element] {
let array = self as NSArray
let result = array.sortedArray(options: .stable) { (left, right) -> ComparisonResult in
let left = left as! Element
let right = right as! Element
if left > right {
return ComparisonResult.orderedAscending
}
if left < right {
return ComparisonResult.orderedDescending
}
return ComparisonResult.orderedSame
}
return result as! [Element]
}
}
I appreciate the elegance of Tom's answer. Harking back to my Perl days I've reworked it to use ComparisonResult and the spaceship (<=>) operator:
extension Sequence {
func sorted(with comparator: (Element, Element) throws -> ComparisonResult) rethrows -> [Element]
{
return try enumerated()
.sorted { (try comparator($0.element, $1.element) || $0.offset <=> $1.offset) == .orderedAscending }
.map { $0.element }
}
}
extension Comparable {
static func <=> (lhs: Self, rhs: Self) -> ComparisonResult {
if lhs < rhs { return .orderedAscending }
if lhs > rhs { return .orderedDescending }
return .orderedSame
}
}
extension ComparisonResult {
static func || (lhs: Self, rhs: #autoclosure () -> Self) -> ComparisonResult {
if lhs == .orderedSame {
return rhs()
}
return lhs
}
}
I'm trying to create an extension but somehow it keeps saying:
Ambiguous reference to member '=='.
class Foo: Equatable {
var string = ""
var number = 0
init(string: String, number: Int) {
self.string = string
self.number = number
}
}
extension Array where Iterator.Element: Foo {
mutating func replace(object: Foo) {
if let index = index(where: { $0.number == object.number}) {
self[index] = object
}
}
}
func ==(lhs: Foo, rhs: Foo) -> Bool {
return lhs.number == rhs.number
}
What am I doing wrong?
Try this:
extension Array where Element: Foo {
mutating func replace(object: Element) {
if let index = index(where: {$0.number == object.number}) {
self[index] = object
}
}
}
To make self[index] = object valid, object needs to be an Element of the Array, which can be any subclass of Foo.
And unfortunately, Swift cannot infer the type of Element from the constraint to Iterator.Element. You may need to declare the constraint to Element directly.
(And to test the code above, I have removed : Equatable from your Foo, which is irrelevant when you use index(where:) in the extension.)
Swift 5 version and support multiple object replacement(add if not exist).
extension Foo {
public static func == (lhs: Object, rhs: Object) -> Bool {
return lhs.id == rhs.id
}
}
extension Array where Element: Foo {
mutating func replace(object: Element) -> Bool {
if let index = firstIndex(where: {$0 == object}) {
self[index] = object
return true
}
return false
}
mutating func replace(objects: Array<Element>) {
objects.forEach { (object) in
if replace(object: object) == false {
self.append(object)
}
}
}
}
I have two different classes / struct and I want to be able to use:
let index = [T].indexOf(U)
Some sample data for playground:
struct Foo: Hashable {
let name: String
var hashValue: Int { return name.hashValue }
}
struct Bar: Hashable {
let name: String
var hashValue: Int { return name.hashValue }
}
func ==(lhs: Foo, rhs: Foo) -> Bool { return lhs.name == rhs.name }
func ==(lhs: Bar, rhs: Bar) -> Bool { return lhs.name == rhs.name }
func ==(lhs: Bar, rhs: Foo) -> Bool { return lhs.name == rhs.name }
Attempts:
let test1 = Foo(name: "John")
let test2 = Foo(name: "Amy")
let test3 = Bar(name: "Mary")
let test4 = Bar(name: "John")
let arrays = [test1, test2]
let result = arrays.indexOf(test3) // cannot convert value of type 'Bar' to expected argument type 'Foo'
Should I just use a protocol instead?
Using protocols:
protocol Names {
var name: String { get set }
}
extension Equatable where Self: Names { }
struct Foo: Names { var name: String }
struct Bar: Names { var name: String }
func ==<T: Names>(lhs: T, rhs: T) -> Bool {
return lhs.name == rhs.name
}
Attempts:
let arrays: [Names] = [test1, test2]
let result = arrays.indexOf(test2) // cannot convert value of type 'Foo' to expected argument type '#noescape (Names) throws -> Bool'
And:
protocol Names: Equatable {
var name: String { get set }
}
let arrays: [Foo] = [test1, test2]
let result = arrays.indexOf(test3) // cannot convert value of type 'Bar' to expected argument type 'Foo'
The issue is that the definition of the operator overload for Equatable is:
func ==(_ lhs: Self, _ rhs: Self) -> Bool
Therefore it is contained to the type of object it is operating upon - in your case Foo or Bar, but not a mix of both. The following declaration will never be used by Equatable:
func ==(lhs: Bar, rhs: Foo) -> Bool { return lhs.name == rhs.name }
Can you change your structs to classes that subclass a common ancestor? Along the lines of:
class FooBar: Hashable {
var name: String!
var hashValue: Int { return name.hashValue }
init() { }
}
class Foo: FooBar {
}
class Bar: FooBar {
}
func ==(lhs: FooBar, rhs: FooBar) -> Bool { return lhs.name == rhs.name }
var test1 = Foo(); test1.name = "John"
var test2 = Foo(); test2.name = "Amy"
var test3 = Bar(); test3.name = "Mary"
var test4 = Bar(); test4.name = "John"
let arrays: [FooBar] = [test1, test2]
let result = arrays.indexOf(test4)
print(result)