How to Sort 1 Array in Swift/Xcode and Reorder Multiple Other Arrays by the Same Keys Changes

How to sort 1 array in Swift / Xcode and reorder multiple other arrays by the same keys changes

You could create an array of indexes in sorted order and use it as a mapping:

var names = [ "Paul", "John", "David" ]
var ages = [ 35, 42, 27 ]

let newOrder = names.enumerate().sort({$0.1<$1.1}).map({$0.0})

names = newOrder.map({names[$0]})
ages = newOrder.map({ages[$0]})

[EDIT] Here's an improvement on the technique :

It's the same approach but does the sorting and assignment in one step.
(can be reassigned to original arrays or to separate ones)

(firstNames,ages,cities,countries,actives) = 
{(
$0.map{firstNames[$0]},
$0.map{ages[$0]},
$0.map{cities[$0]},
$0.map{countries[$0]},
$0.map{actives[$0]}
)}
(firstNames.enumerated().sorted{$0.1<$1.1}.map{$0.0})

[EDIT2] and an Array extension to make it even easier to use if you are sorting in place:

extension Array where Element:Comparable
{
func ordering(by order:(Element,Element)->Bool) -> [Int]
{ return self.enumerated().sorted{order($0.1,$1.1)}.map{$0.0} }
}

extension Array
{
func reorder<T>(_ otherArray:inout [T]) -> [Element]
{
otherArray = self.map{otherArray[$0 as! Int]}
return self
}
}

firstNames.ordering(by: <)
.reorder(&firstNames)
.reorder(&ages)
.reorder(&cities)
.reorder(&countries)
.reorder(&actives)

combining the previous two:

extension Array
{
func reordered<T>(_ otherArray:[T]) -> [T]
{
return self.map{otherArray[$0 as! Int]}
}
}

(firstNames,ages,cities,countries,actives) =
{(
$0.reordered(firstNames),
$0.reordered(ages),
$0.reordered(cities),
$0.reordered(countries),
$0.reordered(actives)
)}
(firstNames.ordering(by:<))

Sorting a Swift array by ordering from another array

Edit: My original approach was shit. This post got a lot of traction, so it's time to give it some more attention and improve it.


Fundamentally, the problem is easy. We have two elements, and we have an array (or any ordered Collection) whose relative ordering determines their sort order. For every element, we find its position in the ordered collection, and compare the two indices to determine which is "greater".

However, if we naively do linear searches (e.g. Array.firstIndex(of:)), we'll get really bad performance (O(array.count)), particularly if the fixed ordering is very large. To remedy this, we can construct a Dictionary, that maps elements to their indices. The dictionary provides fast O(1) look-ups, which is perfect for the job.

This is exactly what HardCodedOrdering does. It pre-computes a dictionary of elements to their orderings, and provides an interface to compare 2 elements. Even better, it can be configured to respond differently to encountering elements with an unknown ordering. It could put them first before everything else, last after everything else, or crash entirely (the default behaviour).

HardCodedOrdering

public struct HardCodedOrdering<Element> where Element: Hashable {
public enum UnspecifiedItemSortingPolicy {
case first
case last
case assertAllItemsHaveDefinedSorting
}

private let ordering: [Element: Int]
private let sortingPolicy: UnspecifiedItemSortingPolicy

public init(
ordering: Element...,
sortUnspecifiedItems sortingPolicy: UnspecifiedItemSortingPolicy = .assertAllItemsHaveDefinedSorting
) {
self.init(ordering: ordering, sortUnspecifiedItems: sortingPolicy)
}

public init<S: Sequence>(
ordering: S,
sortUnspecifiedItems sortingPolicy: UnspecifiedItemSortingPolicy = .assertAllItemsHaveDefinedSorting
) where S.Element == Element {

self.ordering = Dictionary(uniqueKeysWithValues: zip(ordering, 1...))
self.sortingPolicy = sortingPolicy
}

private func sortKey(for element: Element) -> Int {
if let definedSortKey = self.ordering[element] { return definedSortKey }

switch sortingPolicy {
case .first: return Int.min
case .last: return Int.max

case .assertAllItemsHaveDefinedSorting:
fatalError("Found an element that does not have a defined ordering: \(element)")
}
}

public func contains(_ element: Element) -> Bool {
return self.ordering.keys.contains(element)
}

// For use in sorting a collection of `T`s by the value's yielded by `keyDeriver`.
// A throwing varient could be introduced, if necessary.
public func areInIncreasingOrder<T>(by keyDeriver: @escaping (T) -> Element) -> (T, T) -> Bool {
return { lhs, rhs in
self.sortKey(for: keyDeriver(lhs)) < self.sortKey(for: keyDeriver(rhs))
}
}

// For use in sorting a collection of `Element`s
public func areInIncreasingOrder(_ lhs: Element, rhs: Element) -> Bool {
return sortKey(for: lhs) < sortKey(for: rhs)
}
}

Example usage:


let rankOrdering = HardCodedOrdering(ordering: "Private", "Lieutenant", "Captain", "Admiral") // ideally, construct this once, cache it and share it

let someRanks = [
"Admiral", // Should be last (greatest)
"Gallactic Overlord", // fake, should be removed
"Private", // Should be first (least)
]
let realRanks = someRanks.lazy.filter(rankOrdering.contains)
let sortedRealRanks = realRanks.sorted(by: rankOrdering.areInIncreasingOrder) // works with mutating varient, `sort(by:)`, too.

print(sortedRealRanks) // => ["Private", "Admiral"]

Swift2 - Sort multiple arrays based on the sorted order of another INT array

Create a new array of indexes sorted the way you want "descending" and then map the other arrays.

var points:[Int] = [200, 1000, 100, 500]
var people:[String] = ["Harry", "Jerry", "Hannah", "John"]
var peopleIds:[Int] = [1, 2, 3, 4]
var sex:[String] = ["Male", "Male", "Female", "Male"]

//descending order array of indexes
let sortedOrder = points.enumerate().sort({$0.1>$1.1}).map({$0.0})

//Map the arrays based on the new sortedOrder
points = sortedOrder.map({points[$0]})
people = sortedOrder.map({people[$0]})
peopleIds = sortedOrder.map({peopleIds[$0]})
sex = sortedOrder.map({sex[$0]})

I just tested this solution out and it works well.

Swift - Sort array of objects with multiple criteria

Think of what "sorting by multiple criteria" means. It means that two objects are first compared by one criteria. Then, if those criteria are the same, ties will be broken by the next criteria, and so on until you get the desired ordering.

let sortedContacts = contacts.sort {
if $0.lastName != $1.lastName { // first, compare by last names
return $0.lastName < $1.lastName
}
/* last names are the same, break ties by foo
else if $0.foo != $1.foo {
return $0.foo < $1.foo
}
... repeat for all other fields in the sorting
*/
else { // All other fields are tied, break ties by last name
return $0.firstName < $1.firstName
}
}

What you're seeing here is the Sequence.sorted(by:) method, which consults the provided closure to determine how elements compare.

If your sorting will be used in many places, it may be better to make your type conform to the Comparable protocol. That way, you can use Sequence.sorted() method, which consults your implementation of the Comparable.<(_:_:) operator to determine how elements compare. This way, you can sort any Sequence of Contacts without ever having to duplicate the sorting code.

Sorting 2 arrays the same way in swift

Given

let prices = [9, 4, 1]
let names = ["item 1", "item 2", "item 3"]

Solution #1

You can

let sorted = zip(prices, names).sorted { $0.0 < $1.0 }

let sortedPrices = sorted.map { $0.0 } // [1, 4, 9]
let sortedNames = sorted.map { $0.1 } // ["item 3", "item 2", "item 1"]

Solution #2

You should really use a model value.

let prices = [9, 4, 1]
let names = ["item 1", "item 2", "item 3"]

struct Item {
let name: String
let price: Int
}

let sortedItems = zip(names, prices).map(Item.init).sorted { $0.price < $1.price }

// [Item(name: "item 3", price: 1), Item(name: "item 2", price: 4), Item(name: "item 1", price: 9)]

Swift : Sorting three arrays based on a particular array

First of all in Swift we should not name a variable (or constant) after its type, so:

let codes = ["de_DE", "en_US", "en-GB", "es_ES"]
let locales = ["Deutsch", "English", "English UK", "Español"]
let internationals = ["German", "English", "British English", "Spanish"]

Now let's build an array of indexes looking at how codes should be sorted

let indexes = codes.enumerate().sort { $0.element < $1.element }.map { $0.index }

The indexes array contains [0, 2, 1, 3], the i-th element into this array represents the position the i-th of codes should have.

So now we can sort each array based on indexes

let sortedCodes = codes.enumerate().sort { indexes[$0.0] < indexes[$1.0] }.map { $0.element }
let sortedLocales = locales.enumerate().sort { indexes[$0.0] < indexes[$1.0] }.map { $0.element }
let sortedInternationals = internationals.enumerate().sort { indexes[$0.0] < indexes[$1.0] }.map { $0.element }

Output

sortedCodes // ["de_DE", "en-GB", "en_US", "es_ES"]
sortedLocales // ["Deutsch", "English UK", "English", "Español"]
sortedInternationals // ["German", "British English", "English", "Spanish"]

That's it.

Reorder array compared to another array in Swift

You can sort objects in order to follow the order in ids writing

let sorted = objects.sort { ids.indexOf($0.id) < ids.indexOf($1.id) }
// [{id "1"}, {id "2"}, {id "3"}]

Another example

let ids: [String] = ["3", "2", "1"]
let objects: [MyObject] = [MyObject(id: "3"), MyObject(id: "1"), MyObject(id: "2")]

let sorted = objects.sort { ids.indexOf($0.id) < ids.indexOf($1.id) }
// [{id "3"}, {id "2"}, {id "1"}]

This code is in Swift 2.2


Swift 4.2 solution

let sorted = objects.sorted { ids.index(of: $0.id)! < ids.index(of: $1.id)! }

How to sort an array of custom objects by property value in Swift

First, declare your Array as a typed array so that you can call methods when you iterate:

var images : [imageFile] = []

Then you can simply do:

Swift 2

images.sorted({ $0.fileID > $1.fileID })

Swift 3

images.sorted(by: { $0.fileID > $1.fileID })

Swift 5

images.sorted { $0.fileId > $1.fileID }

The example above gives the results in descending order.



Related Topics



Leave a reply



Submit