Swift - Group elements of Array by value (2 by 2,

2019-09-25 06:24发布

EDIT: I'm not asking a function to count an occurence. I'm asking a function to count an occurence 2 by 2, 3 by 3, 10 by 10, etc... this is my problem

I have an array of scores, lets say:

[2,2,3,4,4,4,4,5,6,6,8,8,8,9,10,10]

I would like to have a function that transform this Array into a Dictionary [Int: Int]() to have something like that:

func groupArrayBy(array: Array<Int>, coef: Int) -> Array<Int, Int>{
   // if coef = 2 -> 2 by 2, count occurence
   // Transform the array to:
   // [2: 3, 4: 5, 6: 2, 8: 4, 10: 2]
   // return Dictionary
}

(With coef = 3, it would be: [2: 7, 5: 3, 8: 6] -> 3 by 3)

I found nothing about that. Is it even possible ?

4条回答
Viruses.
2楼-- · 2019-09-25 06:54

I am still a bit confused about the coefficient. However, let's suppose that what you want is to group any value N with the next N+coefficient values.

Then I would start by remapping the original array into the group values:

let items = [2,2,3,4,4,4,4,5,6,6,8,8,8,9,10,10]

let uniqueItems = Set(items)
var itemCoefficientMapping: [Int: Int] = [:]

let coefficient = 3
for item in uniqueItems.sorted() {
    // look whether exists some lower value that is not itself mapped to another value
    let normalizedItem = (item - (coefficient - 1)...item).first {
        uniqueItems.contains($0) && itemCoefficientMapping[$0] == $0
    } ?? item
    itemCoefficientMapping[item] = normalizedItem
}

// count  by mapped value
let counts: [Int: Int] = items.reduce(into: [:]) { result, value in
    result[itemCoefficientMapping[value] ?? value, default: 0] += 1
}

print(counts)
查看更多
3楼-- · 2019-09-25 07:02

Your question is so confusing that what coef means or can we expect the input array is sorted, what output type you want. There's no Array<Int, Int> in Swift.

Assuming the input array is sorted and you want Array<(Int, Int)>, you can write something like this:

func getRanges(_ arr: [Int], _ step: Int) -> [Range<Int>] {
    return stride(from: arr.first!, through: arr.last!, by: step)
        .map {$0..<$0+step}
}
func groupArrayBy(array: Array<Int>, coef: Int) -> [(Int, Int)] {
    let grpArr = getRanges(arr, coef).map {rng in
        (rng.lowerBound, arr.lazy.filter{rng.contains($0)}.count)
    }
    return grpArr
}

print(groupArrayBy(array: arr, coef: 2)) //->[(2, 3), (4, 5), (6, 2), (8, 4), (10, 2)]
print(groupArrayBy(array: arr, coef: 3)) //->[(2, 7), (5, 3), (8, 6)]

My code is not so efficient and there may be someone who can show you more efficient code.

查看更多
做自己的国王
4楼-- · 2019-09-25 07:15

It may have another simpler code as this:

let aa : [Int] = [2,2,3,4,4,4,4,5,6,6,8,8,8,9,10,10]
let mini = aa.min()!
let coeff : Int = 3

let dict = Dictionary(grouping: aa) {
mini + (($0 - mini) / coeff) * coeff
   }.mapValues{$0.count}

print (dict)
查看更多
聊天终结者
5楼-- · 2019-09-25 07:16

Here is my version where I filter on a range based on first value of the array and the coef variable, based on the result I slice away those elements already counted and filter again on the smaller array in a loop. This solution requires the input array to be sorted in ascending order

func group(_ array: [Int], coef: Int) -> [Int: Int] {
    var result:[Int:Int] = [:]

    var start = array[0]
    var end = start + coef - 1
    var arr  = array

    while start <= array[array.count - 1] {
       let count = arr.filter({ $0 >= start && $0 <= end}).count

       result[start] = count
       start = end + 1
       end = start + coef - 1
       arr = Array(arr[count...])
    }
    return result
}

And here is a recursive version of the above function

func group(_ array: [Int], coef: Int) -> [Int: Int] {
    var result:[Int:Int] = [:]
    if array.isEmpty { return result }

    let end = array[0] + coef - 1
    let count = array.filter({ $0 >= array[0] && $0 <= end}).count
    result[array[0]] = count
    result = result.merging(group(Array(array[count...]), coef: coef)) { $1 }
    return result
}
查看更多
登录 后发表回答