在 SWIFT 中对数组中的数字进行计数并按计数排序



有没有一种简单的方法可以按数字计数对数组进行排序?如果一个数字具有相同的计数,则将最高的数字放在第一位。

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

更好的是,您现在可以为序列类型创建另一个扩展。现在他们需要遵守ComparableHashable

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

上面的扩展在内部将数组转换为频率字典,只要求您输入返回Boolclosure。然后,您可以根据需要应用不同的排序。

由于您将带有排序逻辑的闭包传递给此函数,因此SequenceTypeElements不再需要具有可比性。

所有速记的备忘单:

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

sortByFrequencysubArrays的输出进行排序,然后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

最新更新