计算包含相同两个值的数组的数量

时间:2014-10-21 16:05:16

标签: ios arrays dictionary swift

给定Dictionary<String, Arrary<Int>>找到5中第一个Array<Int>条目中有多少个条目具有相同的两个指定值。

例如:

假设:

let numberSeries = [

    "20022016": [07,14,36,47,50,02,05],
    "13022016": [16,07,32,36,41,07,09],
    "27022016": [14,18,19,31,36,04,05],

]

值为736,结果应为2,因为第一个和第二个条目都包含值736条目数组的前5个条目。

我试图通过很多方式实现这一目标,但我无法让它发挥作用。

这是我目前的尝试:

//created a dictionary with (key, values)

let numberSeries = [

"20022016": [07,14,36,47,50,02,05],
"13022016": [16,07,32,36,41,07,09],
"27022016": [14,18,19,31,36,04,05],

]

var a = 07  //number to look for
var b = 36  // number to look for

// SearchForPairAB // search for pair // Doesn't Work.                                  

var ab = [a,b] // pair to look for
var abPairApearedCount = 0

for (kind, numbers) in numberSeries {
    for number in numbers[0...4] {
         if number == ab {           //err: Cannot invoke '==' with argument listof type Int, @Value [Int]
             abPairApearedCount++
         }
     }
 }

这会在行Cannot invoke '==' with argument listof type Int, @Value [Int]

上显示错误:if number == ab

2 个答案:

答案 0 :(得分:2)

您无法使用==来比较IntArray<Int>,从比较的角度来看,这没有任何意义。有很多不同的方法可以实现你想要做的事情。在这种情况下,我可能会使用map / reduce来计算您的配对。

我们的想法是map ab数组中的值到Bool值,取决于值是否在numbers数组中。然后,reduceBool映射到单个值:true,如果他们全部truefalse。如果reduce d值为true,那么我们找到该对,以便我们增加计数。

var ab = [a,b] // pair to look for
var abPairApearedCount = 0

for (kind, numbers) in numberSeries {
    let found = ab.map({ number in
        // find is a built-in function that returns the index of the value
        // in the array, or nil if it's not found
        return find(numbers[0...4], number) != nil
    }).reduce(true) { (result, value: Bool) in
        return result && value
    }
    if found {
        abPairApearedCount++
    }
}

通过使用Swift的一些更简洁的语法,实际上可以进行相当多的压缩:

var ab = [a,b] // pair to look for
var abPairApearedCount = 0

for (kind, numbers) in numberSeries {
    let found = ab.map({ find(numbers[0...4], $0) != nil }).reduce(true) { $0 && $1 }
    if found {
        abPairApearedCount++
    }
}

而且,为了好玩,可以使用reduce代替for-in循环进一步压缩:

var ab = [a,b] // pair to look for
var abPairApearedCount = reduce(numberSeries, 0) { result, series in
    result + (ab.map({ find(series.1[0...4], $0) != nil }).reduce(true) { $0 && $1 } ? 1 : 0)
}

虽然这种情况变得相当难以理解,但我可能会将其中的一部分扩展掉。

答案 1 :(得分:0)

所以这是我的FP解决方案,旨在将问题分解为易于消化和可重复使用的一口大小的块:

首先,我们定义一个将数组修剪为给定长度的仿函数:

func trimLength<T>(length: Int) -> ([T]) -> [T] {
    return { return Array($0[0...length]) }
}

使用此功能,我们可以使用map(array, trimLength(5))

修剪所有元素

现在,我们需要一个谓词来确定一个数组的所有元素是否都在目标数组中:

func containsAll<T:Equatable>(check:[T]) -> ([T]) -> Bool {
    return { target in
        return reduce(check, true, { acc, elem in return acc && contains(target, elem) })
    }
}

这是这里最丑陋的代码,但实际上它只是迭代检查并确保每个元素都在目标数组中。一旦我们得到了这个,我们可以使用filter(array, containsAll([7, 26]))来消除不包含所有目标值的数组的所有元素。

此时,我们可以将整个事物粘合在一起:

filter(map(numberSeries.values, trimLength(5)), containsAll([7, 36])).count

但很长一段的嵌套函数难以阅读,让我们定义一些辅助函数和一个自定义运算符:

func rmap<S:SequenceType, T>(transform:(S.Generator.Element)->T) -> (S) -> [T] {
    return { return map($0, transform) }
}

func rfilter<S:SequenceType>(predicate:(S.Generator.Element)->Bool) -> (S) -> [S.Generator.Element] {
    return { sequence in return filter(sequence, predicate) }
}

infix operator <^> { associativity left }
func <^> <S, T>(left:S, right:(S)->T) -> T {
    return right(left)
}

还有一个方便的功能来计算它的输入:

func count<T>(array:[T]) -> Int {
    return array.count
}

现在我们可以将整个事情浓缩为:

numberSeries.values <^> rmap(trimLength(5)) <^> rfilter(containsAll([7, 36])) <^> count