配列内の重複要素を見つける方法は?電話番号の配列があるので、電話番号で右側から左側に向かって検索を開始し、同様の6つの整数を見つける必要があります。それから私はそれらを印刷する必要があります。
重複を見つけるには、電話番号で相互参照を作成し、それを重複のみに絞り込みます。たとえば、次のことを考慮してください。
let contacts = [
Contact(name: "Rob", phone: "555-1111"),
Contact(name: "Richard", phone: "555-2222"),
Contact(name: "Rachel", phone: "555-1111"),
Contact(name: "Loren", phone: "555-2222"),
Contact(name: "Mary", phone: "555-3333"),
Contact(name: "Susie", phone: "555-2222")
]
Swift 4では、以下を使用して相互参照辞書を作成できます。
let crossReference = Dictionary(grouping: contacts, by: { $0.phone })
または
let crossReference = contacts.reduce(into: [String: [Contact]]()) {
$0[$1.phone, default: []].append($1)
}
次に、重複を見つけます。
let duplicates = crossReference
.filter { $1.count > 1 } // filter down to only those with multiple contacts
.sorted { $0.1.count > $1.1.count } // if you want, sort in descending order by number of duplicates
明らかに、あなたにとって意味のあるモデルタイプを使用してください。ただし、上記では次のContact
タイプを使用します。
struct Contact {
let name: String
let phone: String
}
これを実装するには多くの方法がありますので、上記の実装の詳細に焦点を当てるのではなく、コンセプトに焦点を当てます:いくつかのキー(電話番号など)で相互参照元の配列を構築し、結果をフィルタリングします値が重複するキーのみ。
重複を反映するこの構造を1つの連絡先の配列にフラット化するように聞こえます(相互の重複を識別する構造が失われるため、なぜそうする必要があるのかわかりません)が、それをしたい場合は、flatMap
it:
let flattenedDuplicates = crossReference
.filter { $1.count > 1 } // filter down to only those with multiple contacts
.flatMap { $0.1 } // flatten it down to just array of contacts that are duplicates of something else
Swift 2または3レンディションについては、 この回答の以前のレンディション を参照してください。
〜気分〜。 Int
sの配列が与えられた場合
let x = [1, 1, 2, 3, 4, 5, 5]
let duplicates = Array(Set(x.filter({ (i: Int) in x.filter({ $0 == i }).count > 1})))
// [1, 5]
これは、コンパイラーを含むすべての関係者とあなたにとって恐ろしく効率的です。
私はただ誇示しています。
編集:誰かがこれをダウン票したので、念のため繰り返しますが、本番環境や他の場所でこれを使用しないでください。
プロパティに基づいて配列をフィルタリングするには、次のメソッドを使用できます。
extension Array {
func filterDuplicates(@noescape includeElement: (lhs:Element, rhs:Element) -> Bool) -> [Element]{
var results = [Element]()
forEach { (element) in
let existingElements = results.filter {
return includeElement(lhs: element, rhs: $0)
}
if existingElements.count == 0 {
results.append(element)
}
}
return results
}
}
Robの連絡先の例に基づいて、次のように呼び出すことができます。
let filteredContacts = myContacts.filterDuplicates { $0.name == $1.name && $0.phone == $1.phone }
"Merge sort" を使用して実装することもできますが、1つの変更を加える必要があります。マージ手順中に重複を無視する必要があります。
重複する要素を見つける最も簡単な方法は、電話番号が6桁の数字で、タイプがIntの場合、電話番号の配列をソートし、それをフィルタリングして重複を見つけることです。
var phoneNumbers = [123456, 234567, 345678, 123456, 456789, 135790, 456789, 142638]
func findDuplicates(sortedArray array: [Int]) -> [Int]
{
var duplicates: [Int] = []
var prevItem: Int = 0
var addedItem: Int = 0
for item in array
{
if(prevItem == item && addedItem != item)
{
duplicates.append(item)
addedItem = item
}
prevItem = item
}
return duplicates
}
func sortPhoneNumbers(phoneNumbers: [Int]) -> [Int]
{
return phoneNumbers.sorted({ return $0<$1 })
}
sortPhoneNumbers(phoneNumbers)
findDuplicates(sortPhoneNumbers(phoneNumbers))
さらに、findDuplicatesメソッドをさまざまな方法で実装できます。
セットの使用(Swift 1.2+):
func findDuplicates(array: [Int]) -> [Int]
{
var duplicates = Set<Int>()
var prevItem = 0
for item in array
{
if(prevItem == item)
{
duplicates.insert(item)
}
prevItem = item
}
return Array(duplicates)
}
等々。
Rob's answer に基づいて、重複を見つけるための配列拡張は次のとおりです。
extension Array where Element: Hashable {
func duplicates() -> Array {
let groups = Dictionary(grouping: self, by: {$0})
let duplicateGroups = groups.filter {$1.count > 1}
let duplicates = Array(duplicateGroups.keys)
return duplicates
}
}
アントワーヌの解 inSwift 3 +構文
extension Array {
func filterDuplicates(includeElement: @escaping (_ lhs: Element, _ rhs: Element) -> Bool) -> [Element] {
var results = [Element]()
forEach { (element) in
let existingElements = results.filter {
return includeElement(element, $0)
}
if existingElements.count == 0 {
results.append(element)
}
}
return results
}
}
var numbers = [1,2,3,4,5,6,6,6,7,8,8]
let dups = Dictionary(grouping: numbers, by: {$0}).filter { $1.count > 1 }.keys
@ tikhopの回答と同じですが、配列拡張(Swift 3)として:
extension Array where Element: Comparable & Hashable {
public var duplicates: [Element] {
let sortedElements = sorted { $0 < $1 }
var duplicatedElements = Set<Element>()
var previousElement: Element?
for element in sortedElements {
if previousElement == element {
duplicatedElements.insert(element)
}
previousElement = element
}
return Array(duplicatedElements)
}
}
Reduceを使用する方法を見つけました。コードは次のとおりです(Swift 4):
let testNumbers = [1,1,2,3,4,5,2]
let nondupicate = testNumbers.reduce(into: [Int]()) {
if !$0.contains($1) {
$0.append($1)
} else {
print("Found dupicate: \($1)")
}
}
副作用として、配列に不正な要素がないことを返します。
重複する要素の数を数えたり、文字列配列をチェックしたりするために簡単に変更できます。
私も同様の問題を抱えており、次の方法で克服しました。 (Xcode 8.3.2)
let a = [123456, 234567, 345678, 123456, 456789, 135790, 456789, 142638]
var b = a // copy-on-write so that "a" won't be modified
while let c = b.popLast() {
b.forEach() {
if $0 == c {
Swift.print("Duplication: \(c)")
}
}
}
// Duplication: 456789
// Duplication: 123456
ポイントは、比較の回数ということです。他のものよりも小さいでしょう。
配列内のアイテムの数がNであると仮定します。各ループで、数は1ずつ減少します。したがって、合計数は(N-1)+(N-2)+(N-3)+ ... + 2 + 1 = N *(N-1)/ 2 N = 10の場合、 9 + 8 + ... = 45
対照的に、一部のアルゴリズムの値はN * Nになる場合があります。N= 10の場合は100になります。
それにもかかわらず、ディープコピーまたはシャローコピーのコストを考慮すると、@ Patrick Periniの優れた方法は、状況によってはこれがN * Nであってもこれよりも優れていることに同意します。
編集:
IteratorProtocolの代替方法
let a = [123456, 234567, 345678, 123456, 456789, 135790, 456789, 142638]
var i = a.makeIterator()
while let c = i.next() {
var j = i
while let d = j.next() {
if c == d {
Swift.print("Duplication: \(c)")
}
}
}
// Duplication: 123456
// Duplication: 456789
これはもっと複雑に見えますが、以前と同じ考え方を使用しています。これには、不必要なメモリの割り当てやコピーはありません。
私の懸念は効率です。つまり、UI応答の高速化、バッテリ寿命の延長、メモリフットプリントの縮小などです。背後でSwift競争力のある製品を提供する場合に重要です(-;
すべての重複を保持する非常に簡単な答え
let originalNums = [5, 3, 2, 3 , 7 , 5,3]
var nums = Array(originalNums)
let numSet = Set(nums)
for num in numSet {
if let index = nums.index(of: num) {
nums.remove(at: index)
}
}
出力
[3, 5, 3]