5

So this question is a follow up to this one: swift string permutations allowing the same strings

There, I asked about all possible mutations that I can make with a defined set of strings. What I am trying to do next is to filter out all results that have the same combination, but in a different order.

So if the input is: ["AB", "AC", "CA", "CB"], the output should be ["AB", "AC", "CB"], since "AC" and "CA" have the same building blocks.

So my thought was to first sort each string alphabetically, and maybe then create a Set.

I am already stuck on the first part :(

let array = ["AB", "AC", "DC", "CA", "CB"]
print(type(of: array))
print(array)

let sortedArray = array.map{ $0.sorted() }
print(type(of: sortedArray))
print(sortedArray)

The output is:

Array<String>
["AB", "AC", "DC", "CA", "CB"]
Array<Array<Character>>
[["A", "B"], ["A", "C"], ["C", "D"], ["A", "C"], ["B", "C"]]

While I expected for the sortedArray:

["AB", "AC", "CD", "AC", "BC"]

Then I thought to join the individual strings back together:

print(array.map{ $0.joined() } )

Resulting in ambiguous reference to member 'joined()'

But how to fix this I don't understand.

I also saw this one: swift sort characters in a string where the following code is used:

var nonSortedString = "5121"
var sortedString = String(Array(nonSortedString.characters).sort())

But I don't see how to apply that using map and friends (after converting to Swift 4)

Any help appreciated.

koen
  • 5,383
  • 7
  • 50
  • 89
  • It sounds like you're trying to build a collection of the combinations, not the permutations. It begs the question as to whether you might have been able to build the combinations to begin with, rather than building the permutations first and then transforming/reducing that to the combinations. – Rob Feb 26 '18 at 19:31
  • You are correct - combinations is what I really need, thanks for pointing that out. – koen Feb 26 '18 at 19:38

2 Answers2

21

If you want to take a string, sort its characters, and build a new string from that, in Swift 4 you can:

let string = "foobar"

let sortedString = String(string.sorted())

That results in:

"abfoor"

So, going back to your original problem, you can take you strings, which are a collection of permutations, and build a sorted array of combinations like so:

let permutations = ["AB", "AC", "DC", "CA", "CB"]

// build set of combinations where each string has, itself, been sorted alphabetically

let combinations = Set(permutations.map { String($0.sorted()) })

// convert set (which removed duplicates) back to an array and sort it

let result = Array(combinations).sorted()

That results in:

["AB", "AC", "BC", "CD"]

Rob
  • 415,655
  • 72
  • 787
  • 1,044
3

A different approach... This solution uses another reduce function implementation from the Sequence protocol

let reduced = array.map({ String($0.sorted()) }).reduce(into: [String]() ){ (result, element)  -> Void in
    if !result.contains(element)
    {
        result.append(element)
    }
}.sorted()

print(reduced)

The result is...

["AB", "AC", "BC", "CD"]

Adolfo
  • 1,862
  • 13
  • 19
  • I like this (+1). I was benchmarking this, though, and notice that you can roughly double performance by doing insertion sort instead of reducing and sorting in two different steps. But the `Array(Set(...))` approach is an order of magnitude faster than even that. – Rob Feb 27 '18 at 17:02