Find unique values in a Swift Array

I am building a project that tells me the unique words in a piece of text.

I have my orginal string scriptTextView which I have added each word into the array scriptEachWordInArray

  • Core Data: In XCode how can I set my attribute to be unique?
  • Do swift hashable protocol hash functions need to return unique values?
  • removing duplicates from array in objective c
  • I would now like to create an array called scriptUniqueWords which only includes words that appear once (in other words are unique) in scriptEachWordInArray

    So I’d like my scriptUniqueWords array to equal = [“Silent”,”Holy”] as a result.

    I don’t want to create an array without duplicates but an array that has only values that appeared once in the first place.

    var scriptTextView = "Silent Night Holy Night"
    var scriptEachWordInArray = ["Silent", "night", "Holy", "night"]
    var scriptUniqueWords = [String]()
    
    for i in 0..<scriptEachWordInArray.count {
    
        if scriptTextView.components(separatedBy: "\(scriptEachWordInArray[i]) ").count == 1 {
            scriptUniqueWords.append(scriptEachWordInArray[i])
            print("Unique word \(scriptEachWordInArray[i])")}
    
    }
    

    4 Solutions Collect From Internet About “Find unique values in a Swift Array”

    You can use NSCountedSet

    let text = "Silent Night Holy Night"
    let words = text.lowercased().components(separatedBy: " ")
    let countedSet = NSCountedSet(array: words)
    let singleOccurrencies = countedSet.filter { countedSet.count(for: $0) == 1 }.flatMap { $0 as? String }
    

    Now singleOccurrencies contains ["holy", "silent"]

    swift: 3.0

    lets try It.

    let array = ["1", "1", "2", "2", "3", "3"]
    let unique = Array(Set(array))
    // ["1", "2", "3"]
    

    Filtering out unique words without preserving order

    As another alternative to NSCountedSet, you could use a dictionary to count the the number of occurrences of each word, and filter out those that only occur once:

    let scriptEachWordInArray = ["Silent", "night", "Holy", "night"]
    
    var freqs: [String: Int] = [:]
    scriptEachWordInArray.forEach { freqs[$0] = (freqs[$0] ?? 0) + 1 }
    
    let scriptUniqueWords = freqs.flatMap { $0.1 == 1 ? $0.0 : nil }
    print(scriptUniqueWords) // ["Holy", "Silent"]
    

    This solution, however (as well as the one using NSCountedSet), will not preserve the order of the original array, since a dictionary as well as NSCountedSet is an unordered collection.


    Filtering out unique words while preserving order

    If you’d like to preserve the order from the original array (removing element which appear more than once), you could count the frequencies of each word, but store it in a (String, Int) tuple array rather than a dictionary.

    Making use of the Collection extension from this Q&A

    extension Collection where Iterator.Element: Hashable {
        var frequencies: [(Iterator.Element, Int)] {
            var seen: [Iterator.Element: Int] = [:]
            var frequencies: [(Iterator.Element, Int)] = []
            forEach {
                if let idx = seen[$0] {
                    frequencies[idx].1 += 1
                }
                else {
                    seen[$0] = frequencies.count
                    frequencies.append(($0, 1))
                }
            }
            return frequencies
        }
    }
    
    // or, briefer but worse at showing intent
    extension Collection where Iterator.Element: Hashable {
        var frequencies: [(Iterator.Element, Int)] {
            var seen: [Iterator.Element: Int] = [:]
            var frequencies: [(Iterator.Element, Int)] = []
            for elem in self {
                seen[elem].map { frequencies[$0].1 += 1 } ?? {
                    seen[elem] = frequencies.count
                    return frequencies.append((elem, 1))
                }()
            }
            return frequencies
        }
    }
    

    … you may filter out the unique words of your array (while preserving order) as

    let scriptUniqueWords = scriptEachWordInArray.frequencies
        .flatMap { $0.1 == 1 ? $0.0 : nil }
    
    print(scriptUniqueWords) // ["Silent", "Holy"]
    

    you can filter the values that are already contained in the array:

    let newArray = array.filter { !array.contains($0) }