PlatformDating
PlatformDating

Reputation: 197

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

Sorry for the complex wording of the question. My main experience is with PHP and it has a command called array_multisort. The syntax is below:

bool array_multisort ( array &$array1 [, mixed $array1_sort_order = SORT_ASC [, mixed $array1_sort_flags = SORT_REGULAR [, mixed $... ]]] )

It lets you sort 1 array and the reorder multiple other arrays based on the key changes in the original.

Is there an equivalent command in Swift / Xcode 7.2?

I have currently have a set of arrays:

FirstName Age City Country Active

Active is an array of time in seconds that a user has been active within my app. I would like to order that descending or ascending and the other arrays to change to remain consistent.

Upvotes: 3

Views: 1109

Answers (4)

Alain T.
Alain T.

Reputation: 42143

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:<))

Upvotes: 5

dfrib
dfrib

Reputation: 73176

I believe AlainT:s solution is to prefer, but to extend the variety of options, below follows a solution mimicking what a zip5 method could let us achive (in case we could use zip for zipping together 5 sequences instead of its limit of 2):

/* example arrays */
var firstName: [String] = ["David", "Paul", "Lisa"]
var age: [Int] = [17, 27, 22]
var city: [String] = ["London", "Rome", "New York"]
var country: [String] = ["England", "Italy", "USA"]
var active: [Int] = [906, 299, 5060]

/* create an array of 5-tuples to hold the members of the arrays above.
   This is an approach somewhat mimicking a 5-tuple zip version.        */
var quinTupleArr : [(String, Int, String, String, Int)] = []
for i in 0..<firstName.count {
    quinTupleArr.append((firstName[i], age[i], city[i], country[i], active[i]))
}

/* sort w.r.t. 'active' tuple member */
quinTupleArr.sort { $0.4 < $1.4 }

/* map back to original arrays */
firstName = quinTupleArr.map {$0.0}
age = quinTupleArr.map {$0.1}
city = quinTupleArr.map {$0.2}
country = quinTupleArr.map {$0.3}
active = quinTupleArr.map {$0.4}

Upvotes: 0

FranMowinckel
FranMowinckel

Reputation: 4343

Edit this is valid for 2 arrays:

Adding to @AlainT answer, but using zip:

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

let sortedTuple = zip(names, ages).sort { $0.0.0 < $0.1.0 }

Something more generic:

names.enumerate().sort({$0.1<$1.1}).map({ (name: $0.1, age: ages[$0.0]) })

Upvotes: 1

Hugo Alonso
Hugo Alonso

Reputation: 6824

I would go with @AntonBronnikov suggestion, and put all your properties into an struct, making an Array of that particular struct and then sorting it.

This data is clearly related and it's a cleaner approach.

Upvotes: 1

Related Questions