-1

I want to subtract array1 by array2 Example:

var array1 = ["the", "people", "prefer", "to", "go", "to", "the","sun","beach"]

var array2 = ["the", "people", "prefer", "go", "to", "the", "moon","beach"]

I want Output:

 ["to","sun"]

What I am trying so far:

let reuslt = array1.filter { ! array2.contains($0) }

Output:

 ["sun"]

it's checking to contain a matching item removing all items if it matches but I want to remove one for one.

1
  • Both arrays have “to”, why are you expecting it in the output? Commented Jun 22, 2022 at 16:01

6 Answers 6

1

Just do it on the computer the way you would do it in your brain. Loop through array2 (not array1). For each element of array2, if that element has a firstIndex in array1, remove the element at that index from array1.

for word in array2 {
    if let index = array1.firstIndex(of: word) {
        array1.remove(at: index)
    }
}
Sign up to request clarification or add additional context in comments.

Comments

1

@Thang Phi's answer has the right idea. This is not different, but it works with a level of abstraction that incorporates the "counted set" idea for which Swift doesn't yet provide a built-in type:

import OrderedCollections

public extension Sequence where Element: Hashable {
  /// A version of this sequence without the earliest occurrences of all `elementsToRemove`.
  ///
  /// If `elementsToRemove` contains multiple equivalent values,
  /// that many of the earliest occurrences will be filtered out.
  func filteringOutEarliestOccurrences(from elementsToRemove: some Sequence<Element>) -> some Sequence<Element> {
    var elementCounts = Dictionary(bucketing: elementsToRemove)
    return lazy.filter {
      do {
        try elementCounts.remove(countedSetElement: $0)
        return false
      } catch {
        return true
      }
    }
  }
}
public extension Dictionary where Value == Int {
  /// Create "buckets" from a sequence of keys,
  /// such as might be used for a histogram.
  /// - Note: This can be used for a "counted set".
  @inlinable init(bucketing unbucketedKeys: some Sequence<Key>) {
    self.init(zip(unbucketedKeys, 1), uniquingKeysWith: +)
  }

  /// Treating this dictionary as a "counted set", reduce the element's value by 1.
  /// - Throws: If `countedSetElement` is not a key.
  @inlinable mutating func remove(countedSetElement: Key) throws {
    guard let count = self[countedSetElement] else { throw AnyError() }
    self[countedSetElement] = count == 1 ? nil : count - 1
  }
}
/// `zip` a sequence with a single value, instead of another sequence.
@inlinable public func zip<Sequence: Swift.Sequence, Constant>(
  _ sequence: Sequence, _ constant: Constant
) -> some Swift.Sequence<(Sequence.Element, Constant)> {
  zip(sequence, **ModuleName**.sequence(constant))
}
/// An infinite sequence of a single value.
@inlinable public func sequence<Element>(_ element: Element) -> some Sequence<Element> {
  let getSelf: (Element) -> Element = \.self
  return sequence(first: element, next: getSelf)
}
/// A nondescript error.
public struct AnyError: Error & Equatable {
  public init() { }
}

Comments

0

What you effectively want to do is this for loop

for item2 in array2 {
    for i in 0..<array1.count {
        if item2 == array1[i] {
            array1.remove(at: i)
            break
        }
    }
}

Filter works in exactly this way except it doesn't break on the first item but continues to remove all items.

You can also put this into a one liner like this with map instead of filter: array2.map({guard let i = array1.firstIndex(of: $0) else {return}; array1.remove(at: i)})

Comments

0

Like above answers, it is ok for a loop contains findIndex and remove from array.

But in other world, I think if the array is too large, the complexity of firstIndex(of:) and remove(at:) cause time and CPU too much for this task - Heat of device can raise a lots too. You can minimize it by using dictionary.

This is an another approach:

func findTheDiff(_ compareArr: [String], _ desArr: [String]) -> [String] {
    var resultArr : [String] = []
    
    var dict : [String: Int] = [:]
    
    for word in compareArr {
        if dict[word] == nil {
            dict[word] = 1
        } else {
            dict[word] = dict[word]! + 1
        }
    }
    
    for checkWord in desArr {
        if dict[checkWord] != nil && dict[checkWord]! > 0 {
            dict[checkWord] = dict[checkWord]! - 1
            continue
        }
        
        resultArr.append(checkWord)
    }
    
    return resultArr
}

Usage:

var array1 = ["the", "people", "prefer", "to", "go", "to", "the","sun","beach"]

var array2 = ["the", "people", "prefer", "go", "to", "the", "moon","beach"]
        
var result = self.findTheDiff(array2, array1)
print(result) // ["to", "sun"]

You can find the complexity of firstIndex, remove(at:) below: https://developer.apple.com/documentation/swift/array/firstindex(of:) https://developer.apple.com/documentation/swift/array/remove(at:)-1p2pj

Comments

-1

Your example seems a little confusing / incomplete on the output. But sounds like you could do something like this:

extension Array where Element: Hashable {
    func difference(from other: [Element]) -> [Element] {
        let thisSet = Set(self)
        let otherSet = Set(other)
        return Array(thisSet.symmetricDifference(otherSet))
    }
}

let names1 = ["the", "people", "prefer", "to", "go", "to", "the","sun","beach"]
let names2 = ["the", "people", "prefer", "go", "to", "the", "moon","beach"]
let difference = names1.difference(from: names2)

print(Array(difference)) // ["sun", "moon"]

Using an extension will of course make this code available to all of your arrays in your project. Since we convert the arrays into Sets, duplicates are removed, which may be a issue in your use case.

This Array extension was taken form: https://www.hackingwithswift.com/example-code/language/how-to-find-the-difference-between-two-arrays A vital resoruce for all things swift, especially SwiftUI.

1 Comment

This doesn't print the solution he wants ["to","sun"] but prints ["sun", "moon"].
-1

Simple subtraction method that works for Equatable types including String.

extension Array where Element: Equatable {
    func subtracting(_ array: Array<Element>) -> Array<Element> {
        var result: Array<Element> = []
        var toSub = array
        
        for i in self {
            if let index = toSub.firstIndex(of: i) {
                toSub.remove(at: index)
                continue
            }
            else {
                result.append(i)
            }
        }
        return result
    }
}


let first = [1, 1, 2, 3, 3, 5, 6, 7, 7]
let second = [2, 2, 3, 4, 4, 5, 5, 6]
let result = first.subtracting(second)

//print result 
//[1, 1, 3, 7, 7]

Comments

Your Answer

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.