1

I have an array like:

var arr = [4,1,5,5,3]

I want to fetch subset from the array based on the occurrence of elements in it.

For example:

Elements with frequency 1 is {4,1,3}
Elements with frequency 2 is {5,5}

I followed this StackOverflow question but unable to figure out how to do the above thing.

Is there any way I can do this?

Rumin
  • 3,787
  • 3
  • 27
  • 30

3 Answers3

1

You can use an NSCountedSet to get the count of all elements in arr, then you can build a Dictionary, where the keys will be the number of occurencies for the elements and the values will be Arrays of the elements with key number of occurences. By iterating through Set(arr) rather than simply arr to build the Dictionary, you can make sure that repeating elements are only added once to the Dictionary (so for instance with your original example, 5 wouldn't be added twice as having a frequency of 2).

For the printing, you just need to iterate through the keys of the Dictionary and print the keys along with their corresponding values. I just sorted the keys to make the printing go in ascending order of number of occurences.

let arr = [4,1,5,5,3,2,3,6,2,7,8,2,7,2,8,8,8,7]
let counts = NSCountedSet(array: arr)
var countDict = [Int:[Int]]()
for element in Set(arr) {
    countDict[counts.count(for: element), default: []].append(element)
}
countDict

for freq in countDict.keys.sorted() {
    print("Elements with frequency \(freq) are {\(countDict[freq]!)}")
}

Output:

Elements with frequency 1 are {[4, 6, 1]}
Elements with frequency 2 are {[5, 3]}
Elements with frequency 3 are {[7]}
Elements with frequency 4 are {[2, 8]}

Swift 3 version:

let arr = [4,1,5,5,3,2,3,6,2,7,8,2,7,2,8,8,8,7]
let counts = NSCountedSet(array: arr)
var countDict = [Int:[Int]]()
for element in Set(arr) {
    if countDict[counts.count(for: element)] != nil {
        countDict[counts.count(for: element)]!.append(element)  
    } else {
        countDict[counts.count(for: element)] = [element]
    }
}

for freq in countDict.keys.sorted() {
    print("Elements with frequency \(freq) are {\(countDict[freq]!)}")
}
Dávid Pásztor
  • 51,403
  • 9
  • 85
  • 116
  • Note that using NSCountedSet doesn't keep the array order – Leo Dabus Jan 17 '18 at 00:41
  • @Dávid Pásztor I am getting error "Cannot subscript a value of type '[Int : [Int]]' with an index of type '(Int, default: [Any])' (aka '(Int, default: Array)')" – Rumin Jan 17 '18 at 00:52
  • @Rumin Are you sure you're using the exact same code as mine? Which line throws that errors? The code runs just fine in a Playground for me. Btw you also have to make sure `Foundation` is imported (if you import `UIKit`, that also imports `Foundation`) to use `NSCountedSet`. – Dávid Pásztor Jan 17 '18 at 00:55
  • Yes, I am running the same exact code in playground.. I have also imported Foundation. Please check if you have provided the same code as you are running. – Rumin Jan 17 '18 at 01:05
  • @Rumin yes, this is the exact same code I'm running in my Playground. It also runs perfectly on the IBM Swift sandbox, [have a look yourself](http://swift.sandbox.bluemix.net/#/repl/5a5ea1debf9f220dd80e9711). What Swift version are you using? My code uses Swift 4. – Dávid Pásztor Jan 17 '18 at 01:09
  • I think swift version is generating this error. I am on swift 3 – Rumin Jan 17 '18 at 01:10
  • @Rumin you should always include in your question what Swift version you're using. Updated and tested my code on Swift 3, check my answer – Dávid Pásztor Jan 17 '18 at 01:13
1

You just need to get the occurrences of the elements and filter the elements that only occurs once or more than once as shown in this answer:

extension Array where Element: Hashable {
    // Swift 4 or later
    var occurrences: [Element: Int] {
        return reduce(into: [:]) { $0[$1, default: 0] += 1 }
    }
    // // for Swift 3 or earlier
    // var occurrences: [Element: Int] {
    //     var result: [Element: Int] = [:]
    //     forEach{ result[$0] = (result[$0] ?? 0) + 1}
    //     return result
    // }
    func frequencies(where isIncluded: (Int) -> Bool) -> Array {
        return filter{ isIncluded(occurrences[$0] ?? 0) }
    }
}

Playground Testing:

let arr = [5, 4, 1, 5, 5, 3, 5, 3]

let frequency1 = arr.frequencies {$0 == 1}       // [4, 1]
let frequency2 = arr.frequencies {$0 == 2}       // [3, 3]
let frequency3orMore = arr.frequencies {$0 >= 3} // [5, 5, 5, 5]
Leo Dabus
  • 229,809
  • 59
  • 489
  • 571
0

This is it:

func getSubset(of array: [Int], withFrequency frequency: Int) -> [Int]
{
    var counts: [Int: Int] = [:]

    for item in array
    {
        counts[item] = (counts[item] ?? 0) + 1
    }

    let filtered  = counts.filter{ $0.value == frequency}

    return Array(filtered.keys)
}

This is pure Swift (not using good old Next Step classes) and is using ideas from the SO link you supplied.

The counts dictionary contains the frequencies (value) of each of the int-values (key) in your array: [int-value : frequency].

meaning-matters
  • 21,929
  • 10
  • 82
  • 142
  • 1
    It’s important to note that this function doesn’t actually solve the whole issue. You need to know what frequencies are present in your array to be able to use this function effectively. Without knowing this, your function’s performance becomes quite bad if one wants to find the all subsets of the array. – Dávid Pásztor Jan 17 '18 at 09:35
  • @DávidPásztor I think this is what's asked for. The present frequencies are available in `counts`, so in case that's needed too, it's very simple to mould this logic into another shape. – meaning-matters Jan 17 '18 at 10:20