Swift: map set of values A to B and B to A

346 Views Asked by At

The task:

Consider a set of values, e.g. 0, 1, 2, now imagine two of these sets and a bijective relation between them.

How can I implement this in Swift, encapsulated in a data structure?

Clarification and examples:

An example mapping could look like this:

0 <-> 1
1 <-> 2
2 <-> 0

The classic bidirectional hashmap doesn't fit to this use case well, as the values on both sides are non-unique.

The data structure should allow querying from both sides:

let ds = DS(...)
let ds.right(from: 1) // 2
let ds.left(from: 0) // 2

What is the simplest way of implementing such data structure? What existing data types I can base my implementation on?

UPDATE:

What does "the values on both sides are non-unique” The values on the "Left" side are unique within that side, as well as the values on the "Right" side. However, if the value is present in one side, it will always be present in the other. Hence, the values are not unique.

Can you give an example with non-unique values, and the expected results of right(from:) and left(from:) in the case of non-uniqueness?

To clarify, all the values the left side has are 0,1,2. The right side also has 0,1,2.

query examples:

ds.rightFrom(left: 2) -> 0
ds.rightFrom(left: 0) -> 1


ds.leftFrom(right: 0) -> 2
ds.leftFrom(right: 1) -> 0
3

There are 3 best solutions below

0
Martin R On BEST ANSWER

A bijective function from a set to itself is a permutation. If the set consists of consecutive integers starting at zero then the permutation can be represented as an array.

In your case, the mapping from [0, 1, 2] to itself defined by

0 -> 1, 1 -> 2, 2 -> 0

would be represented as the array [1, 2, 0]. The “left-to-right” mapping then becomes a subscript operation:

let perm = [1, 2, 0]

print(perm[1]) // 2

The “right-to-left” mapping is the inverse permutation, and can also be represented as an array:

func inversePermution(of perm: [Int]) -> [Int]? {
    var inverse: [Int] = Array(repeating: -1, count: perm.count)
    for (idx, elem) in perm.enumerated() {
        // Check for valid entries:
        guard elem >= 0 && elem < perm.count else { return nil }
        // Check for duplicate entries:
        guard inverse[elem] == -1 else { return nil }
        // Set inverse mapping:
        inverse[elem] = idx
    }
    return inverse
}

(This is just to demonstrate the general idea. Of course you can make this an Array extension method, or define a Permutation type with this and more methods.)

In your example:

if let invPerm = inversePermution(of: perm) {
    print(invPerm) // [2, 0, 1]

    print(invPerm[2]) // 1
}
9
PGDev On

You can use zip(_:_:) on array, i.e.

let arr1 = [0,1,2]
let arr2 = [01,2,0]

let result = Array(zip(arr1,arr2))

print(result) //Output: [(0, 1), (1, 2), (2, 0)]
12
Richard Topchii On

Code I've finished with:

import Foundation

public struct BidirectionalMapNonUnique<Left, Right> where Left: Hashable, Right: Hashable {
    private let ltr: [Left: Right]
    public let rtl: [Right: Left]

    public init(_ ltrMapping: [Left: Right]) {
        var rtlPending = [Right: Left]()
        for (key, value) in ltrMapping {
            rtlPending[value] = key
        }
        self.ltr = ltrMapping
        self.rtl = rtlPending
    }

    public func leftFrom(right: Right) -> Left {
        return rtl[right]!
    }

    public func rightFrom(left: Left) -> Right {
        return ltr[left]!
    }
}


let example = BidirectionalMapNonUnique<Int, Int>([0:10, 1:11, 2:12])

print(example.leftFrom(right: 11)) // Prints 1
print(example.rightFrom(left: 0)) // Prints 10