0

I have a Nested string array, below it's just an example a nested string array, This nested array cant be sorted alphabetically, because of that I created a Numbered array that corresponds to each nested string array.

To be clear the nested array cannot be sorted alphabetically so dont provide answers regarding that.

What Im trying to do, But don't understand how to achieve is to sort my int array from smallest to largest.

From there or at the same time short the nest array based on the Int arrays index changed

Nested String Array Example (Array Size Is Dynamic)

[["ddd"],["aaa"],["ggg"]]

Int Array Example (Array Size Is Dynamic)

[557, 147, 355]

After Sorting Both would look like this

[["aaa"],["ggg"], ["ddd"]]
[147, 355, 557]
user3277468
  • 141
  • 2
  • 11
  • Do both arrays have the same size? – pawello2222 Jul 05 '20 at 22:08
  • yes the number array corresponds to the nested array @pawello2222 – user3277468 Jul 05 '20 at 22:10
  • Does this answer your question? [Sorting a Swift array by ordering from another array](https://stackoverflow.com/questions/43056807/sorting-a-swift-array-by-ordering-from-another-array) – pawello2222 Jul 05 '20 at 22:14
  • 1
    My first thought would be to create an array of an appropriate struct and then just sort that. Independent arrays that have an implicit relationship based on indices is a bit of a code smell. If you can't do that then `zip` your two arrays into an array of tulles, sort that and then unzip it back to two arrays via `reduce` – Paulw11 Jul 05 '20 at 22:27

3 Answers3

2

You can zip both arrays together and sort it by the second element of the tuple. Then you just need to map the first element:

let a = [["ddd"],["aaa"],["ggg"]]
let b = [557, 147, 355]

let sorted = zip(a, b).sorted { $0.1 < $1.1 }

let sortedA = sorted.map(\.0)
sortedA // [["aaa"], ["ggg"], ["ddd"]]

let sortedB = sorted.map(\.1)
sortedB  // [147, 355, 557]
Leo Dabus
  • 229,809
  • 59
  • 489
  • 571
1

You can get an index from one array and sort using values from the second array:

let a = [["ddd"],["aaa"],["ggg"]]
let b = [557, 147, 355]

let sortedA = a.sorted { b[a.firstIndex(of: $0)!] < b[a.firstIndex(of: $1)!] }
// prints [["aaa"], ["ggg"], ["ddd"]]

let sortedB = b.sorted()
// prints [147, 355, 557]

This one-liner:

let sortedA = a.sorted { b[a.firstIndex(of: $0)!] < b[a.firstIndex(of: $1)!] }

is a shorter form of this:

let sortedA = a.sorted { item1, item2 in
    let item1Index = a.firstIndex(of: item1)!
    let item2Index = a.firstIndex(of: item2)!
    return b[item1Index] < b[item2Index]
}
pawello2222
  • 46,897
  • 22
  • 145
  • 209
  • This is what I was looking for, I tried something like this but failed earlier. Can you explain whats happening in the `{}` – user3277468 Jul 05 '20 at 22:23
  • 1
    @user3277468 Of course, I was doing just that :) I updated my answer. – pawello2222 Jul 05 '20 at 22:27
  • Be aware that this is an O(n2) solution. This may not matter if your arrays are small. Also, it will have a problem with duplicate values in `a` – Paulw11 Jul 05 '20 at 22:35
  • O(n squared) you mean? Yeah, that's bad for anything but small data-sets. – Duncan C Jul 05 '20 at 22:40
  • Yes, sorry, missed a ^ – Paulw11 Jul 05 '20 at 23:37
  • I've started using superscript unicode characters, but I couldn't get that to work yesterday. This morning on my work computer I'm able to generate the string `O(n²)` I wanted to use in the first place. – Duncan C Jul 06 '20 at 12:52
0

@pawello2222's solution works but it has n squared performance, meaning it will be very slow for large data-sets.

Optimized sort algorithms tend to have n * log(n) performance, which is much, much better.

The following code will use the native sorted method in Swift, which should have good performance:

let a = [["ddd"],["aaa"],["ggg"]]
let b = [557, 147, 355]


let bIndexes = b

    //turn the array b into a new array of tuples 
    //containing elements and "offsets" (indexes)
    .enumerated() 

    .sorted { $0.element < $1.element }  //Sort the new array by elements
    .map { $0.offset }                   //Map the new array to just index.

print("bIndexes = \(bIndexes)")

//Loop through the array of indexes and use the indexes to fetch elements in sorted order.
bIndexes.forEach { print (b[$0], a[$0]) } 

This outputs:

bIndexes = [1, 2, 0]
147 ["aaa"]
355 ["ggg"]
557 ["ddd"]

Saving the results into sorted arrays is a simple matter of mapping:

let newA = bIndexes.map { a[$0] }
let newB = bIndexes.map { b[$0] }

Edit:

Or better yet, use LeoDabus' solution:

let a = [["ddd"],["aaa"],["ggg"]]
let b = [557, 147, 355]

let sortedIndexes = b.indices.sorted { b[$0] < b[$1] }

let sortedA = sortedIndexes.map { a[$0] }
let sortedB = sortedIndexes.map { b[$0] }

or if you only want the strings sorted based on the integer values:

let sortedA = b.indices.sorted { b[$0] < b[$1] }
.map { a[$0] }
Duncan C
  • 128,072
  • 22
  • 173
  • 272
  • You can also sort the collection indices directly. No need to enumerate the collection and it would also work on subsequences `b.indices.sorted { b[$0] < b[$1] }` – Leo Dabus Jul 05 '20 at 23:14
  • @LeoDabus , that's the best solution yet. You should post that as a separate answer so it gets the attention it deserves. – Duncan C Jul 06 '20 at 12:49