Sort array elements based on their frequency - ios

I need to sort an array of elements based on their frequency, for example:
Input array: [1, 6, 6, 6, 6, 4, 3, 5, 5, 5, 2, 2]
Expected output: [1, 3, 4, 2, 2, 5, 5, 5, 6, 6, 6, 6]
I tried with the code below:
var set: NSCountedSet = [1, 6, 6, 6, 6, 4, 3, 5, 5, 5, 2, 2]
var dictionary = [Int: Int]()
set.forEach { (item) in
dictionary[item as! Int] = set.count(for: item)
}
dictionary.keys.sorted()
print(dictionary)
Description: As 1, 3, 4 occur only once, they are shown at the beginning, 2 occurs two times, 5 three times, 6 four times. And [1, 3, 4] are sorted among them.
Expected result: Time complexity should be O(n)

You can achieve the results in O(nlogn) time by first creating a Dictionary containing the number of occurrences for each element (O(n)), then calling sorted on the Array (Swift uses Introsort, which is O(nlogn)) and using the values from the previously created Dictionary for the sorting. The elements of your array need to be Comparable for sorting to work and Hashable to be able to store them in a Dictionary, which provides O(1) element lookup.
extension Array where Element: Comparable & Hashable {
func sortByNumberOfOccurences() -> [Element] {
let occurencesDict = self.reduce(into: [Element:Int](), { currentResult, element in
currentResult[element, default: 0] += 1
})
return self.sorted(by: { current, next in occurencesDict[current]! < occurencesDict[next]!})
}
}
[1, 6, 6, 6, 6, 4, 3, 5, 5, 5, 2, 2].sortByNumberOfOccurences() // [1, 4, 3, 2, 2, 5, 5, 5, 6, 6, 6, 6]
The above solution preserves the order of elements that occur an equal number of times. If you actually want to sort such elements based on their compared values (which is what your sample output does), you can modify the closure in sorted like below:
return self.sorted(by: {occurencesDict[$0]! <= occurencesDict[$1]! && $0 < $1})
Or even shorter, comparing tuples for sorting:
return self.sorted(by: {(occurencesDict[$0]!,$0) < (occurencesDict[$1]!,$1)})
which produces the sample output you provided, [1, 3, 4, 2, 2, 5, 5, 5, 6, 6, 6, 6]

You can try
let dd = [1, 6, 6, 6, 6, 4, 3, 5, 5, 5, 2, 2]
let res = dd.sorted { f, s in
dd.filter { $0 == f }.count < dd.filter { $0 == s }.count
}
print(res) // [1, 4, 3, 2, 2, 5, 5, 5, 6, 6, 6, 6]

There is no way to sort with O(n) time complexity. Look at the worst case complexity for popular algorithms at Wikipedia.
The better worst-case time complexity is O(nlogn). Here is how we can solve it with O(nlogn) time complexity:
let array = [1, 6, 6, 6, 6, 4, 3, 5, 5, 5, 2, 2]
extension Array where Element: Comparable & Hashable {
func countableSorted() -> [Element] {
var counts = [Element: Int]()
// O(n)
for element in self {
counts[element] = (counts[element] ?? 0) + 1
}
// I think that standart method uses O(nlogn) time complexity.
// O(nlogn) + O(n) approximately equal to O(nlogn).
let sorted = counts.sorted { item1, item2 -> Bool in
if item2.value > item1.value {
return true
}
if item2.value == item1.value {
return item2.key > item1.key
}
return false
}
var result = [Element]()
// O(n)
for item in sorted {
let items = Array(repeating: item.key, count: item.value)
result.append(contentsOf: items)
}
// Total time complexity for worst case scenario is O(nlogn)
return result
}
}
print(array.countableSorted())
// Output: [1, 3, 4, 2, 2, 5, 5, 5, 6, 6, 6, 6]

You can try the below code, this worked properly.
var inputArray = [1, 6, 6, 6, 6, 4, 3, 5, 5, 5, 2, 2]
inputArray.sort()
let freq = inputArray.sorted { f, s in
inputArray.filter { $0 == f}.count < inputArray.filter { $0 == s}.count
}
print(freq)
Not sure about the time complexity.

I want to add a solution in O(n)
Sorting takes O(nLogn) but this question can also be solved without using sorting by help of HashMap in Java because it contains the pairs sorted in accordance to the key.
import java.util.*;
class Simple
{
public static void main(String[] arg)
{ int inputArray[] = {1, 6, 6, 6, 6, 4, 3, 5, 5, 5, 2, 2};
Map<Integer,Integer> map = new HashMap<Integer,Integer>();
Map<Integer,List<Integer>> map2 = new HashMap<Integer,List<Integer>>();
for(int i: inputArray)
{
if(map.get(i) == null){
map.put(i, 1) ;
}
else{
int a = map.get(i);
map.put(i,a+1);
}
}
// using for-each loop for iteration over Map.entrySet()
for (Map.Entry<Integer,Integer> entry : map.entrySet()) {
if(map2.get(entry.getValue()) == null){
map2.put(entry.getValue(), new ArrayList<Integer>()) ;
}
map2.get(entry.getValue()).add(entry.getKey());
}
for(Map.Entry<Integer,List<Integer>> entry : map2.entrySet()){
for(int j=0; j<entry.getValue().size(); j++){
for(int i=0; i<entry.getKey(); i++){
System.out.print(entry.getValue().get(j) + " ");
}
}
}
}
}
In First for loop I am iterating through array saving pair of (value,Occurrence) in map1(HashMap). This will take O(n) as HashMap put operation(insertion) takes O(1).
In second for loop I am iterating map1 and inserting pair of (occurrence, list of numbers in the given array with that occurrence) in map2(HashMap2).
Now in last for loop I am iterating through map2 and printing all the lists one by one it means I am printing each element of given array once i.e. I am iterating through the list of each key and printing each element of the list key number of times. So this would also take O(n).
more about HashMap
Time Complexity : O(n)
Swift Version of above code
extension Array where Element: Comparable & Hashable {
func sortByNumberOfOccurences() -> [Element] {
let occurencesDict = self.reduce(into: [Element:Int](), { currentResult, element in
currentResult[element, default: 0] += 1
})
let dict = occurencesDict.sorted(by: {$0.0 < $1.0})
var dictioanary = [Int:Array]()
for (element,occurence) in dict {
if dictioanary[occurence] == nil
{
dictioanary[occurence] = Array()
}
dictioanary[occurence]?.append(element)
}
var resultArray = Array()
let finalDict = dictioanary.sorted(by: {$0.0 < $1.0})
for (frequency,allValuesOccuringWithThisFrequncy) in finalDict {
for i in allValuesOccuringWithThisFrequncy
{
var j = 0
while(j < frequency)
{
resultArray.append(i)
j = j + 1
}
}
}
print(resultArray)
return resultArray
}
}
Time Complexity in Swift O(nLogn)

var inputArray = [1, 6, 6, 6, 6, 4, 3, 5, 5, 5, 2, 2]
var map:[Int: Int] = [:]
for element in inputArray {
let count = map[element]
if count == nil {
map[element] = 1
} else {
map[element] = count! + 1
}
}
var keysArray = map.keys
let sortedKeys = keysArray.sorted { (number1, number2) -> Bool in
if map[number1]! == map[number2]! {
return number1 < number2
} else {
return map[number1]! < map[number2]!
}
}
var finalArray: [Int] = []
for element in sortedKeys {
for _ in 1...map[element]! {
finalArray.append(element)
}
}
print(finalArray)
Time Complexity: O(nlogn)

Try this solution. It worked for me like a charm :)
func numberOfOccurences(in array: [Int], of element: Int) -> Int {
let object = NSCountedSet(array: array)
return object.count(for: element)
}
var inputArray = [1, 6, 6, 6, 6, 4, 3, 5, 5, 5, 2, 2]
var uniqueElements = Array(Set(inputArray))
var otherArray: [Int] = []
var duplicateElements = uniqueElements.filter { (element) -> Bool in
return (inputArray.contains(element) && numberOfOccurences(in: inputArray, of: element) > 1)
}
uniqueElements = uniqueElements.filter({ !duplicateElements.contains($0) }).sorted()
for item in duplicateElements {
let occurences = numberOfOccurences(in: inputArray, of: item)
for _ in 0...occurences - 1 {
otherArray.append(item)
}
}
otherArray = otherArray.sorted()
duplicateElements.removeAll()
let mergedArray = uniqueElements + otherArray
print(mergedArray)

I think this kind of sorting can be achieved in O(n), with something like the following:
let input = [1, 6, 6, 6, 6, 4, 3, 5, 5, 5, 2, 2]
// build the frequency dictionary (easy task)
let frequencies = input.reduce(into: [:]) { $0[$1] = ($0[$1] ?? 0) + 1 }
// allocate a 2-D array of ints, each item in this array will hold the numbers that
// appear I times in the input array
let frequencyTable: [[Int]] = frequencies.reduce(into: Array(repeating: [Int](), count: input.count)) {
// substracting one as arrays are zero indexed
// we can't get of of bounds here since the maximum frequency is the
// number of elements in the input array
// Also replicating the numbers by their frequency, to have
// the same contents as in the input array
$0[$1.value - 1] += Array(repeating: $1.key, count: $1.value)
}
// lastly, simply flatten the 2-D array to get the output we need
let output = frequencyTable.flatMap { $0 }
print(output)
Sample result:
[4, 1, 3, 2, 2, 5, 5, 5, 6, 6, 6, 6]
Note that the order of numbers with the same frequency might differ based on how the dictionary hash function works.
Also we sacrifice space (allocated 2-D array) in favour of time.
The frequencyTable contents will look similar to this (again the order of 1, 4, 3 might differ):
[[4, 3, 1], [2, 2], [5, 5, 5], [6, 6, 6, 6], [], [], [], [], [], [], [], []]

Related

Merge sub-array in Swift

I have an array and I want to merge it.
This is the array:
let numbers = [ [ 1, 2, 3 ], [ 4, 5, 6 ] ]
I need a output like this:
let result = [ [ 1, 4 ], [ 2, 5 ], [ 3, 6 ] ]
let numbers = [[1,2,3], [4,5,6]]
let result = zip(numbers[0], numbers[1]).map { [$0.0, $0.1]}
print(result) // -> [[1, 4], [2, 5], [3, 6]]
If the array has more elements the following would work.
let numbers = [[1,2,3], [4,5,6], [7,8,9]]
var result : [[Int]] = []
for n in 0...numbers.first!.count-1{
result.append(numbers.compactMap { $0[n] })
}
print(result) // -> [[1, 4, 7], [2, 5, 8], [3, 6, 9]]
You can use the zip global function, which, given 2 sequences, returns a sequence of tuples and then obtain an array of tuples by using the proper init.
var xAxis = [1, 2, 3, 4]
var yAxis = [2, 3, 4, 5]
let pointsSequence = zip(xAxis, yAxis)
let chartPoints = Array(pointsSequence)
print(chartPoints)
And then you can access the tuples like this :
let point = chartPoints[0]
point.0 // This is the 1st element of the tuple
point.1 // This is the 2nd element of the tuple

Swift logic - Data structure | Sort the items in Array based on similar number | Interview question [duplicate]

This question already has answers here:
Sort array elements based on their frequency
(8 answers)
Closed 12 months ago.
How to sort an integer array based on a duplicate values count. here less number of duplicates should come first.
input [5, 2, 1, 2, 4, 4, 1, 1, 2, 3, 3, 6]
OutPut [5, 6, 4, 4, 3, 3, 2, 2, 2, 1, 1, 1]
Using Martin's comment, here is another approach which aims to reduce the number of loops and conditions we write ourselves by using some functions provided by swift.
// Input
let numbers = [1, 1, 1, 2, 2, 2, 3, 3, 4, 4, 5, 6]
// Count the occurrences based on Martin's comment
let countDict = numbers.reduce(into: [:], { $0[$1, default: 0] += 1 } )
// Get a sorted array of the countDict keys, sorted by value which
// is the number of occurrences
let sortedKeys
= countDict.keys
.sorted { countDict[$0, default: 0] < countDict[$1, default: 0] }
// Initialize an empty array to hold the final sorted numbers
var sortedNumbers: [Int] = []
// Add the elements into the sortedNumbers with in their desired order
for key in sortedKeys {
sortedNumbers.append(contentsOf: repeatElement(key,
count: countDict[key, default: 0]))
}
// prints [5, 6, 4, 4, 3, 3, 1, 1, 1, 2, 2, 2] based on the above input
print(sortedNumbers)
let numbers = [5,2,1,2,4,4,1,1,2,3,3,6]
let sortedNumber = numbers.sorted()
print("Input: ",sortedNumber)
var dict = [Int: Int]()
for item in sortedNumber {
let isExist = dict.contains(where: {$0.key == item})
if !isExist {
dict[item] = 1
} else {
if let value = dict[item] {
dict[item] = value + 1
}
}
}
var finalArray = [Int]()
let sortedArray = dict.sorted { (first, second) -> Bool in
return first.value < second.value
}
for d in sortedArray {
for _ in 1...d.value {
finalArray.append(d.key)
}
}
print("Output: ",finalArray)
Input: [1, 1, 1, 2, 2, 2, 3, 3, 4, 4, 5, 6]
Output: [5, 6, 4, 4, 3, 3, 2, 2, 2, 1, 1, 1]

Calculate sum of every 5 elements in array of Integer in Swift iOS

In Swift 3, how can we calculate sum of every 5 elements in array of Int.
For example, we have an array [1,2,3,4,5,6,7,8,9,0,12,23]
1+2+3+4+5 = 15
6+7+8+9+0 = 30
12+23+0+0+0 = 35
The result something like this [15,30,35]
Here is my solution in playgroud:
//: Playground - noun: a place where people can play
import UIKit
var arr = [1,1,1,1,1,2,2,2,2,2,3,3,3,3,3]
let chunkSize = 5
let chunks = stride(from: 0, to: arr.count, by: chunkSize).map {
Array(arr[$0..<min($0 + chunkSize, arr.count)])
}
print(chunks)
var summ = chunks.map { $0.reduce(0, {$0 + $1}) }
print(summ)
OUTPUT:
[[1, 1, 1, 1, 1], [2, 2, 2, 2, 2], [3, 3, 3, 3, 3]]
[5, 10, 15]
Take a look at:
Finding sum of elements in Swift array

How to multiply each Int value in an array by a constant in Swift?

Say, I have an array [20, 2, 3]
How can I multiply each Int value of this array in Swift?
So 2 x array becomes [40, 4, 6], 3 x array becomes [60, 6, 9] and so on?
You can use .map():
let values = [20, 2, 3]
let doubles = values.map { $0 * 2 }
let triples = values.map { $0 * 3 }
If you want to do the update in-place:
var values = [20, 2, 3]
values.enumerated().forEach { index, value in
values[index] = value * 2
}
// values is now [40, 4, 6]
You can use a for loop.
var array = [20, 2, 3]
let multiplier = 2
for i in 0..<array.count {
array[i] = array[i] * multiplier
}

The Swift Programming Language Book Chapter Section on Control Flow Experiment 3

I am working through the first chapter of the book and can't figure out the experiment:
Add another variable to keep track of which kind of number was the
largest, as well as what that largest number was.
This is the code from the book:
let interstingNumbers = [
"Prime": [2, 3, 5, 7, 11, 13, 17],
"Fibonacci": [1, 1, 2, 3, 5, 8],
"Square": [1, 4, 9, 16, 25, 36],
]
var largest = 0
for (kind, numbers) in interstingNumbers {
for number in numbers {
if number > largest {
largest = number
}
}
}
largest
Like this:
let interstingNumbers = [
"Prime": [2, 3, 5, 7, 11, 13, 17],
"Fibonacci": [1, 1, 2, 3, 5, 8],
"Square": [1, 4, 9, 16, 25, 36],
]
var largest = 0
var largestKind = ""
for (kind, numbers) in interstingNumbers {
for number in numbers {
if number > largest {
largest = number
largestKind = kind
}
}
}
largest
largestKind
So basically you'd want to put kind somewhere at the same time as you put number into largest, e.g. into a variable called largestKind.
The question asked to keep track of the value and type not to just display the type of the final answer.
I think this gets closer:
let interestingNumbers = [
"Prime": [2, 3, 5, 7, 11, 13],
"Fibonacci": [1, 1, 2, 3, 5, 8],
"Square": [1, 4, 9, 16, 25],
]
var largest = 0
var largestType = [String]()
var largestValues = [Int]()
for (kind, numbers) in interestingNumbers {
for number in numbers {
if number > largest {
largest = number
largestValues.append(largest)
largestType.append(kind)
}
}
}
print(largest)
print(largestType)
print(largestValues)
And now with tuples:
let interestingNumbers = [
"Prime": [2, 3, 5, 7, 11, 13],
"Fibonacci": [1, 1, 2, 3, 5, 8],
"Square": [1, 4, 9, 16, 25],
]
var largest = 0
var largestValues: [(type: String, value: Int)] = []
for (kind, numbers) in interestingNumbers {
for number in numbers {
if number > largest {
largest = number
largestValues.append(type: kind, value: number)
}
}
}
print(largest)
print(largestValues)
let interestingNumbers = [ "Prime" : [2,3,5,7,11,13],
"Fibonacci" : [1,1,2,3,5,8,13],
"Square" : [1,4,9,16,25,36],
]
var largest = 0
var largestKind = ""
for (kind, numbers) in interestingNumbers {
for number in numbers {
if number > largest {
largest = number
largestKind = kind
}
}
}
print(largest, largestKind)
outputs : 36 Sqaure
let interestingNumbers = [
"Prime": [2, 3, 5, 7, 11, 13],
"Fibonacci": [1, 1, 2, 3, 5, 8],
"Square": [1, 4, 9, 16, 25],
]
var largest = 0
var largestNumbers = [
"Prime":0,
"Fibonacci":0,
"Square":0,
]
for (kind, numbers) in interestingNumbers {
for number in numbers {
if number > largest {
largest = number
}
if(number > largestNumbers[kind]!){
largestNumbers[kind] = number;
}
}
}
print(largest)
print(largestNumbers)
Result is:
25
["Fibonacci": 8, "Square": 25, "Prime": 13]

Resources