在 Swift 数组中查找唯一值

4
我正在构建一个项目,用于找出文本中的唯一单词。
我有一个原始字符串scriptTextView,我已将每个单词添加到数组 scriptEachWordInArray 中。
现在,我想创建一个名为 scriptUniqueWords 的数组,其中只包括在 scriptEachWordInArray 中出现一次(也就是唯一的)的单词。
因此,我希望我的 scriptUniqueWords 数组等于 ["Silent","Holy"]。 我不想创建没有重复项的数组,而是想要一个仅包含第一次出现的值的数组。
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])")}

}

2
尝试过使用 Set 吗? - jtbandes
2
https://dev59.com/Q18e5IYBdhLWcg3wdqGT#29904817, https://dev59.com/aV4c5IYBdhLWcg3wuMJ7 - Bek
1
@jtbandes @Bek 谢谢,但是使用“Set”技术是否会删除所有重复项,使得每个单词只出现一次,而不是可能出现多次。夜晚将只出现在数组中一次,而不是两次。然而我希望只隔离出唯一的值。我已尝试过这种方法了,或者我错过了什么。 - flakedev
重新开启因为我相信这个问题与其他链接的问题不同。然而,我仍然认为您可以使用集合来解决这个问题。 - jtbandes
@jtbandes 当然,如果你认为我错了,解决重复问题是非常合理的。 - matt
4个回答

7
您可以使用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 }

现在,singleOccurrences 包含 ["holy", "silent"]

7

Swift

lets try It.

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

2

过滤不保留顺序的独特单词

除了使用NSCountedSet之外,您还可以使用字典来计算每个单词的出现次数,并过滤那些只出现一次的单词:

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"]

然而,这种解决方案(以及使用NSCountedSet的方案)将无法保留原始数组的顺序,因为字典和NSCountedSet都是无序集合。

保留顺序的过滤唯一单词

如果您想保留原始数组的顺序(删除出现多次的元素),则可以计算每个单词的频率,但将其存储在(String, Int)元组数组中,而不是字典中。

利用此Q&A中的Collection扩展

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
    }
}

您可以筛选出数组中的唯一单词(保留顺序),如下:

let scriptUniqueWords = scriptEachWordInArray.frequencies
    .flatMap { $0.1 == 1 ? $0.0 : nil }

print(scriptUniqueWords) // ["Silent", "Holy"]

使用NSCountedSet来实现此功能。 - Alexander
5
NSCountedSet是一个不错的Foundation方法;我认为多元化对于SO答案很有趣(因此上面的字典方法被称为“另一种选择”)。还有一个有用的方法,可以保留原始数组的顺序。 - dfrib
1
@dfri,你可能会对这个感兴趣 https://stackoverflow.com/a/46376175/2303865 - Leo Dabus

-1

你可以过滤掉已经包含在数组中的值:

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

这不起作用,因为array已经包含了所有它自己的元素。因此表达式!array.contains($0)总是返回false。 - Martin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接