有没有一种简单的方法可以按数字计数对数组进行排序?如果一个数字具有相同的计数,则将最高的数字放在第一位。
[2,8,2,6,1,8,2,6,6]
to
[6,6,6,2,2,2,8,8,1]
您正在寻找的是一种获取值频率的方法。只要这些值Hashable
此函数就会起作用:
它扩展了Element
Hashable
的所有序列类型,因此Int
数组将起作用。
extension SequenceType where Generator.Element : Hashable {
func frequencies() -> [Generator.Element:Int] {
var results : [Generator.Element:Int] = [:]
for element in self {
results[element] = (results[element] ?? 0) + 1
}
return results
}
}
然后你可以这样做:
let alpha = [2,8,2,6,1,8,2,6,6]
let sorted = alpha.frequencies().sort {
if $0.1 > $1.1 { // if the frequency is higher, return true
return true
} else if $0.1 == $1.1 { // if the frequency is equal
return $0.0 > $1.0 // return value is higher
} else {
return false // else return false
}
}
更好的是,您现在可以为序列类型创建另一个扩展。现在他们需要遵守Comparable
和Hashable
extension SequenceType where Generator.Element : protocol<Hashable,Comparable> {
func sortByFrequency() -> [Generator.Element] {
// the same sort function as before
let sorted = self.frequencies().sort {
if $0.1 > $1.1 {
return true
} else if $0.1 == $1.1 {
return $0.0 > $1.0
} else {
return false
}
}
// this is to convert back from the dictionary to an array
var sortedValues : [Generator.Element] = []
sorted.forEach { // for each time the value was found
for _ in 0..<$0.1 {
sortedValues.append($0.0) // append
}
}
return sortedValues
}
}
您对所有这些的最终用法将如下所示:
let sorted = alpha.sortByFrequency() // [6, 6, 6, 2, 2, 2, 8, 8, 1]
超级干净的:)
如果你更喜欢更接近sort
本身的函数,你也可以使用这个:
extension SequenceType where Generator.Element : Hashable {
func sortedFrequency(@noescape isOrderedBefore: ((Self.Generator.Element,Int), (Self.Generator.Element,Int)) -> Bool) -> [Generator.Element] {
let sorted = self.frequencies().sort {
return isOrderedBefore($0,$1) // this uses the closure to sort
}
var sortedValues : [Generator.Element] = []
sorted.forEach {
for _ in 0..<$0.1 {
sortedValues.append($0.0)
}
}
return sortedValues
}
}
上面的扩展在内部将数组转换为频率字典,只要求您输入返回Bool
的closure
。然后,您可以根据需要应用不同的排序。
由于您将带有排序逻辑的闭包传递给此函数,因此SequenceType
的Elements
不再需要具有可比性。
所有速记的备忘单:
$0 // first element
$1 // second element
$0.0 // value of first element
$0.1 // frequency of first element
排序:
let sortedB = alpha.sortedFrequency {
if $0.1 > $1.1 {
return true
} else if $0.1 == $1.1 {
return $0.0 > $1.0
} else {
return false
}
} // [6, 6, 6, 2, 2, 2, 8, 8, 1]
我不确定这是否是最有效的方法,但我认为它相当优雅:
extension Array where Element: Equatable {
func subArrays() -> [[Element]] {
if self.isEmpty {
return [[]]
} else {
let slice = self.filter { $0 == self[0] }
let rest = self.filter { $0 != self[0] }
return rest.isEmpty
? [slice]
: [slice] + rest.subArrays()
}
}
func sortByFrequency(secondarySort: ((Element, Element) -> Bool)? = nil) -> [Element] {
return self.subArrays()
.sort { secondarySort?($0[0], $1[0]) ?? false }
.sort { $0.count > $1.count }
.flatMap { $0 }
}
}
let nums = [2,8,2,6,1,8,2,6,6]
print(nums.sortByFrequency(>)) // [6, 6, 6, 2, 2, 2, 8, 8, 1]
该函数subArrays
只是将数组分解为原始数组中每个值的子数组数组 - 即,您将获得您提供的输入的[[2,2,2],[8,8],[6,6,6],[1]]
。
sortByFrequency
对subArrays
的输出进行排序,然后flatMap
得到答案。
编辑:我修改了sortByFrequency
以添加可选的secondarySearch
参数。这允许您控制如何对以相同频率出现的项目进行排序。或者,只需接受默认nil
,它们将不会按频率以外的任何内容进行排序。
另外,我修改了扩展名以指示Element
只需要符合Equatable
,而不是Comparable
。
//: Playground - noun: a place where people can play
import UIKit
var arr1 = [2,8,2,6,1,8,2,6,6]
var arr2 = [6,6,6,2,2,2,8,8,1]
var counting = [Int: Int]()
// fill counting dictionary
for num in arr1 {
if counting[num] != nil {
counting[num]!++
} else {
counting[num] = 1
}
}
// [6: 3, 2: 3, 8: 2, 1: 1]
print(counting)
func order(i1: Int, i2: Int) -> Bool {
let count1 = counting[i1]
let count2 = counting[i2]
// if counting is the same: compare which number is greater
if count1 == count2 {
return i1 > i2
} else {
return count1 > count2
}
}
// [6, 6, 6, 2, 2, 2, 8, 8, 1]
print(arr1.sort(order))
print(arr2)
在字典中使用分组:
var entries = [1,2,3,3,1,3,5,6,3,4,1,5,5,5,5]
extension Sequence where Element : Hashable {
func byFrequency() -> [Element] {
Dictionary(grouping: self, by: {$0}).sorted{ (a, b) in
a.value.count > b.value.count
}.map { $0.key}
}
}
print(entries.byFrequency().first)
打印 5