Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I sort an array of structs by multiple values?

Tags:

sorting

swift

I already have code to sort by 1 value as shown below, but I'm wondering how to sort using multiple values? I would like to sort by set and then by someString.

One is an integer, and one is a string in this case. I had considered converting the integer to a string and then concatenating them, but thought there must be a better way because I may have 2 integers to sort by in the future.

struct Condition {
    var set = 0
    var someString = ""
}

var conditions = [Condition]()

conditions.append(Condition(set: 1, someString: "string3"))
conditions.append(Condition(set: 2, someString: "string2"))
conditions.append(Condition(set: 3, someString: "string7"))
conditions.append(Condition(set: 1, someString: "string9"))
conditions.append(Condition(set: 2, someString: "string4"))
conditions.append(Condition(set: 3, someString: "string0"))
conditions.append(Condition(set: 1, someString: "string1"))
conditions.append(Condition(set: 2, someString: "string6"))

// sort
let sorted = conditions.sorted { (lhs: Condition, rhs: Condition) -> Bool in
    return (lhs.set) < (rhs.set)
}

// printed sorted conditions
for index in 0...conditions.count-1 {
    println("\(sorted[index].set) - \(sorted[index].someString)")
}
like image 691
Aaron Bratcher Avatar asked Nov 20 '14 13:11

Aaron Bratcher


People also ask

How to sort an array of objects by multiple properties in JavaScript?

Sort an array of objects by multiple properties in JavaScript 1 Suppose, we have an array of objects like this − 2 We are required to sort the above array on the following criteria − 3 If dnf is true, 4 If issue is true, 5 Rest should be sorted by score, and if the scores are equal, by id

What is structure sorting?

In Structure sorting, all the respective properties possessed by the structure object are sorted on the basis of one (or more) property of the object. In this example, marks of students in different subjects are provided by user.

How many values can be selected from an array of structs?

In the case of Array of Structs, the column can be selected directly and it will result in only one row: As above, there is only one row with multiple values for each Struct key. For example, address_history.status has three values [“current”, “previous”, “birth”].

What is the difference between a struct and an array?

Arrays in BigQuery, like in any other language, are a collection of elements of the same data type. For example, this is what an Array address_history might look like: A struct is a data type that has attributes in key-value pairs, just like a dictionary in Python.


3 Answers

I'm not yet proficient in Swift, but the basic idea for a multiple-criteria sort is:

let sorted = conditions.sorted { (lhs: Condition, rhs: Condition) -> Bool in
    if lhs.set == rhs.set {
        return lhs.someString < rhs.someString
    }
    return (lhs.set) < (rhs.set)
}
like image 177
Cyrille Avatar answered Oct 26 '22 05:10

Cyrille


Even though the previous answers are perfectly fine in the requested case, I'd like to put a more general approach for that:


infix operator &lt=> {
associativity none
precedence 130
}
func &lt=> &ltT: Comparable>(lhs: T, rhs: T) -> NSComparisonResult {
    return lhs &lt rhs ? .OrderedAscending : lhs == rhs ? .OrderedSame : .OrderedDescending
}
private func _sortedLexicographically&ltS: SequenceType&gt(source: S, comparators: [(S.Generator.Element, S.Generator.Element) -> NSComparisonResult]) -> [S.Generator.Element] {
    return sorted(source, { lhs, rhs in
        for compare in comparators {
            switch compare(lhs, rhs) {
            case .OrderedAscending: return true
            case .OrderedDescending: return false
            case .OrderedSame: break
            }
        }
        return false
    })
}
public func sortedLexicographically&ltS: SequenceType>(source: S, comparators: [(S.Generator.Element, S.Generator.Element) -> NSComparisonResult]) -> [S.Generator.Element] {
    return _sortedLexicographically(source, comparators)
}
extension Array {
    func sortedLexicographically(comparators: [(Element, Element) -> NSComparisonResult]) -> [Element] {
        return _sortedLexicographically(self, comparators)
    }
}

from here it's quite easy to do an ordering like requested:


struct Foo {
    var foo: Int
    var bar: Int
    var baz: Int
}
let foos = [Foo(foo: 1, bar: 2, baz: 3), Foo(foo: 1, bar: 3, baz: 1), Foo(foo: 0, bar: 4, baz: 2), Foo(foo: 2, bar: 0, baz: 0), Foo(foo: 1, bar: 2, baz: 2)]
let orderedFoos = foos.sortedLexicographically([{ $0.foo &lt=> $1.foo }, { $0.bar &lt=> $1.bar }, { $0.baz &lt=> $1.baz }])

If this kind of comparison for that type is intrinsic to the type itself instead of being an one place-only sorting you need, you can follow the more stdlib-like approach and extending Comparable instead:


extension Foo: Comparable {}
func == (lhs: Foo, rhs: Foo) -> Bool {
    return lhs.foo == rhs.foo && lhs.bar == rhs.bar && lhs.baz == rhs.baz
}
func &lt (lhs: Foo, rhs: Foo) -> Bool {
    let comparators: [(Foo, Foo) -> NSComparisonResult] = [{ $0.foo &lt=> $1.foo }, { $0.bar &lt=> $1.bar }, { $0.baz &lt=> $1.baz }]
    for compare in comparators {
        switch compare(lhs, rhs) {
        case .OrderedAscending: return true
        case .OrderedDescending: return false
        case .OrderedSame: break
        }
    }
    return false
}
let comparableOrderedFoos = sorted(foos)

There would be another possible approach which is making a LexicographicallyComparable protocol which says which Comparable fields and in which priority they do have, but unfortunately I can't think of a way to make it without using variadic generics, which are not supported in Swift as 2.0, while maintaining the type safety typical to Swift code.

like image 44
DeFrenZ Avatar answered Oct 26 '22 05:10

DeFrenZ


You would compare someString if the set values were the same, otherwise, use your existing comparison:

let sorted = conditions.sorted { (lhs: Condition, rhs: Condition) -> Bool in
    if lhs.set == rhs.set {
        return lhs.someString < rhs.someString
    } else {
        return lhs.set < rhs.set
    }
}
like image 26
Rob Avatar answered Oct 26 '22 06:10

Rob