0

I am substracting two Array of Integers. I did the substraction using sets :

let numbersA = [1, 2, 3]
let numbersB = [3, 4, 5]
Set(numbersA).subtracting(numbersB)

but I then realized that I have multiplicity in my arrays, and I should take it into account. What is the data structure for multisets in Swift?

Jeremy Cochoy
  • 2,480
  • 2
  • 24
  • 39

2 Answers2

1

In swift 5, there is no Multiset implementation in the core library. You can reproduce the Multiset behavior by using a dictionary [Element:Multiplicity].

Your code will be:

let numbersA = [1, 2, 3, 3]
let numbersB = [3, 4, 5]
Set(numbersA).subtracting(numbersB) // [1, 2, 3]

As pointed out by Leo Dabus in comment, this gives you an unordered collection as a result.


You can find a good tutorial on multisets at https://github.com/raywenderlich/swift-algorithm-club/tree/master/Multiset

Here is a ready to use implementation of multiset. I added to it the substracting implementation. Notice it can be considered an incomplet implementation of a Multiset since, for exemple, it doesn't extends the Collection protocol.

//
//  Multiset.swift
//  Multiset
//
//  Created by Simon Whitaker on 28/08/2017.
//
//  Extended by Jeremy Cochoy on 17/11/2019

import Foundation

public struct Multiset<T: Hashable> {
  private var storage: [T: UInt] = [:]
  public private(set) var count: UInt = 0

  public init() {}

  public init<C: Collection>(_ collection: C) where C.Element == T {
    for element in collection {
      self.add(element)
    }
  }

  public mutating func add (_ elem: T) {
    storage[elem, default: 0] += 1
    count += 1
  }

  public mutating func remove (_ elem: T) {
    if let currentCount = storage[elem] {
      if currentCount > 1 {
        storage[elem] = currentCount - 1
      } else {
        storage.removeValue(forKey: elem)
      }
      count -= 1
    }
  }

  public func isSubSet (of superset: Multiset<T>) -> Bool {
    for (key, count) in storage {
      let supersetcount = superset.storage[key] ?? 0
      if count > supersetcount {
        return false
      }
    }
    return true
  }

  public func count(for key: T) -> UInt {
    return storage[key] ?? 0
  }

  public var allItems: [T] {
    var result = [T]()
    for (key, count) in storage {
      for _ in 0 ..< count {
        result.append(key)
      }
    }
    return result
  }

  public func subtracting(_ elems: [T]) -> Multiset<T> {
    var resultSet = self
    elems.forEach { resultSet.remove($0) }
    return resultSet
  }
}

// MARK: - Equatable
extension Multiset: Equatable {
  public static func == (lhs: Multiset<T>, rhs: Multiset<T>) -> Bool {
    if lhs.storage.count != rhs.storage.count {
      return false
    }
    for (lkey, lcount) in lhs.storage {
      let rcount = rhs.storage[lkey] ?? 0
      if lcount != rcount {
        return false
      }
    }
    return true
  }
}

// MARK: - ExpressibleByArrayLiteral
extension Multiset: ExpressibleByArrayLiteral {
  public init(arrayLiteral elements: T...) {
    self.init(elements)
  }
}
Jeremy Cochoy
  • 2,480
  • 2
  • 24
  • 39
  • Please explain what you have added/changed to the original solution and why. Right now I find this answer incomplete since I have to visually compare this answer with the linked page to find what is changed. – Joakim Danielson Nov 17 '19 at 13:41
  • 1
    Note that the result will be an unordered collection – Leo Dabus Nov 17 '19 at 21:09
  • @JoakimDanielson I only added a substracting method. One could still improve this class by adding a union method for example. I mentioned the modifications in the answer. – Jeremy Cochoy Nov 19 '19 at 13:45
  • I did figure that out already, I meant that you should improve your answer by better describing what you have done. – Joakim Danielson Nov 19 '19 at 13:47
0

As you say, swift does not have Multiset as a native type. I implemented a fairly thorough implementation of Multiset in Swift here:

https://github.com/Cortado-J/Multiset

and full documentation of that library with usage examples here:

https://cortado-j.github.io/Multiset/Structs/Multiset.html

Cortado-J
  • 2,035
  • 2
  • 20
  • 32