从Swift中的Array中获取3个唯一项 - 每个项都具有唯一值

时间:2015-08-28 11:34:04

标签: arrays swift unique

我有一组键/值对,想要检索随机数量的项目。但是,我要求值是不同的,并且不要多次返回相同的项目。

例如:

Apple     1
Banana    1
Orange    2
Kiwi      2
Pear      3
Pineapple 4

我可能想要检索3个独特的项目。即所有3个都有不同的值,同一个项目不会返回两次。

在Swift中这样做最有效的方法是什么?

4 个答案:

答案 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);