我有一组键/值对,想要检索随机数量的项目。但是,我要求值是不同的,并且不要多次返回相同的项目。
例如:
Apple 1
Banana 1
Orange 2
Kiwi 2
Pear 3
Pineapple 4
我可能想要检索3个独特的项目。即所有3个都有不同的值,同一个项目不会返回两次。
在Swift中这样做最有效的方法是什么?
答案 0 :(得分:1)
编辑:修正答案以处理没有3个不同值的情况。在这种情况下,返回的更少。
将Swift 2与Xcode 7 beta 6一起使用
由于我不清楚这里如何声明数组是两个不同的可能版本。在每种情况下,这个想法都是一样的。
// in this version the array is declared as follows:
let myArray01 = [["Apple",1], ["Banana", 2], ["Orange", 2], ["Kiwi",2], ["Pear",3],["Pineapple",4]]
var myArrayCopy01 = myArray01 // a mutable copy of the array from which items can be safely removed
var counter01:UInt32 = 5 // the number of items in the array
var myResult01 = [Array<NSObject>]()
var interimSelection01: [NSObject]
while myResult01.count < 3 && counter01 > 0 { // the number of randomly selected items we want
interimSelection01 = myArrayCopy01.removeAtIndex(Int(arc4random_uniform(counter01--))) // random selection is removed from mutable copy & counter01--
if !(myResult01.map{$0[1]}).contains(interimSelection01[1]) {
myResult01.append(interimSelection01) // if we don't already have 1 with that value
}
}
print(myResult01)
// in this version the pair is an array of tuples:
let myArray02: [(fruit: String, value: Int)] = [("Apple",1), ("Banana", 2), ("Orange", 2), ("Kiwi",2), ("Pear",3),("Pineapple",4)]
var myArrayCopy02 = myArray02 // a mutable copy of the array from which items can be safely removed
var counter02:UInt32 = 5 // the number of items in the array
var myResult02 = [(fruit: String, value: Int)]()
var interimSelection02: (fruit: String, value: Int)
while myResult02.count < 3 && counter02 > 0 { // the number of randomly selected items we want
interimSelection02 = myArrayCopy02.removeAtIndex(Int(arc4random_uniform(counter02--))) // random selection is removed from mutable copy & counter02--
if !(myResult02.map({$0.value}).contains(interimSelection02.value)) {
myResult02.append(interimSelection02) // if we don't already have 1 with that value
}
}
print(myResult02)
答案 1 :(得分:1)
使用Fisher-Yates混洗算法的一部分,我在CollectionType
上进行了扩展,以从集合中返回Array
n
个随机选择的元素。适用于任何类型的集合(请参阅示例)。复杂性为O(n)
。如果n
大于集合中的元素数,它不会崩溃但会随机返回所有元素。在Swift 2.0 beta 6中测试:
import Darwin
func randomUpTo(n: Int) -> Int {
return Int(arc4random_uniform(UInt32(n)))
}
extension CollectionType {
func chooseRandom(n : Int = Int.max) -> [Generator.Element] {
var values = Array(self)
for index in values.indices.dropFirst().reverse().prefix(n) {
swap(&values[randomUpTo(index)], &values[index])
}
return Array(values.suffix(n))
}
}
示例:
(0...20).chooseRandom(10) // [16, 20, 2, 7, 11, 13, 18, 9, 17, 4]
[1, 3, 5, 7, 9, 11].chooseRandom() // [9, 11, 3, 5, 7, 1]
[
("Apple" , 1),
("Banana" , 1),
("Orange" , 2),
("Kiwi" , 2),
("Pear" , 3),
("Pineapple", 4)
].chooseRandom(3) // [("Apple", 1), ("Pineapple", 4), ("Kiwi", 2)]
编辑:复杂性确实是O(n)
,但如果集合在复制时包含大量元素,则性能可能会很差。我现在正在研究一个懒惰版本,修复了这个问题。
答案 2 :(得分:0)
如果您的元素是可以清除的,则可以使用Set。
import Foundation
extension CollectionType where Generator.Element : Hashable, Index == Int {
func sample(n: Int) -> Set<Generator.Element> {
var result = Set<Generator.Element>()
while result.count < n {
let i = Int(arc4random_uniform(UInt32(count)))
result.insert(self[i])
}
return result
}
}
如果您的元素不可清除,您可以使用索引来确保唯一性:
extension CollectionType where Index == Int {
func sample(n: Int) -> [Generator.Element] {
var result = Set<Int>()
let c = UInt32(count)
while result.count < n {
result.insert(Int(arc4random_uniform(c)))
}
return result.map { i in self[i] }
}
}
此处的第二种方法假设您从中采样的集合不包含重复项。
答案 3 :(得分:-1)
您可以使用数据结构。
Map<String, Integer> newMap = new HashMap<String, Integer>();
List<Integer> valueList = new ArrayList<Integer>(3);
for (Map.Entry<String, Integer> entryMap : map.entrySet()) {
if (!valueList.contains(entryMap.getValue())
&& valueList.size() < 3) {
valueList.add(entryMap.getValue());
newMap.put(entryMap.getKey(), entryMap.getValue());
}
}
System.out.println(newMap);