3

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.

Gareth
5,2636 gold badges47 silver badges73 bronze badges
asked Feb 8, 2016 at 15:56
1
  • 2
    Why not define a struct type that will hold properties firstName, age, city, country, active. Then have an array of that structs, and sort that array the way you need? Commented Feb 8, 2016 at 16:14

4 Answers 4

5

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:<))
answered Feb 8, 2016 at 16:16
4
  • Cool solution. Let me see if I understand how it works. enumerate() maps the original names array to an array of tuples of the form (index, object). Then you sort those tuples, where 0ドル.1 means first object being compared, 2nd entry in the tuple (name string). You map the result of the sort to return an array of the index entries of each tuple. Commented Feb 8, 2016 at 16:29
  • Thank you Alain T. Your code worked perfectly for my needs. A single line solution is great. Commented Feb 8, 2016 at 23:34
  • A follow up question: What is the "type" of newOrder? Commented Feb 9, 2016 at 0:16
  • It's an array of indices so: [Int] Commented Feb 9, 2016 at 0:42
1

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.

answered Feb 8, 2016 at 16:26
1

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]) })
answered Feb 8, 2016 at 16:35
4
  • While AlainT:s answer generalizes to the OP:s example of more than 2 arrays (indices in sorted order can be applied to each of them), zip is limited to pairs (2-tuples), and not really an option in this context. Commented Feb 8, 2016 at 16:36
  • zip were my first approach as well, some 30 minutes ago, but it ended up in a manual 5-zip instead, not very pretty :) Commented Feb 8, 2016 at 16:55
  • @dfri credits go to @AlainT anyway, the trick is enumerate. I think we just prefer to return a tuple instead :) Commented Feb 8, 2016 at 16:58
  • Yeah I think his solution is the most general in this case. I added my manual zip5-solution as well, for the sake of completion. Commented Feb 8, 2016 at 17:01
0

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}
answered Feb 8, 2016 at 17:00
2
  • just a typo: quinTupleArr.count == 0. And if you use named tuples... but that's just a preference Commented Feb 8, 2016 at 17:05
  • @FranMowinckel Ah thanks. I'll leave the tuples unnamed for this technical example. Commented Feb 8, 2016 at 17:08

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.