Implementing counting sort with custom objects - swift

I want to implement a counting sort but want to implement this with custom objects. This is in Swift, but the implementation details are not too important.
I want to sort an object which basically just holds an Integer (key) and a character.
class ToSort : CustomStringConvertible {
var description: String {
return String(num)
}
var num : Int
var val : Character
init(_ num: Int, _ val : Character) {
self.num = num
self.val = val
}
}
Now usually the frequencies of integers are stored (https://www.geeksforgeeks.org/counting-sort/). I want to store my objects - I'm not sure how to do this. I've spent some time looking for implementations (with custom objects - I've got a link just above here for the standard implementation), cannot find one and cannot work out how I should implement this...
I think I can do this by implementing a dictionary (hashmap), but if I'm doing that is the advantage of using a counting sort lost?

You can try this algorithm i implemented it to use custom object might give you an idea of how its done,
struct Foo {
var number: Int
var name: String
}
enum CountingSortError: Error {
case arrayEmpty
}
func countingSort(array: [Foo]) throws -> [Foo] {
guard array.count > 0 else {
throw CountingSortError.arrayEmpty
}
// Step 1
// Create an array to store the count of each element
let maxElement = array.max{ a, b in a.number < b.number } ?? Foo(number: 0, name: "Zero")
var countArray = [Int](repeating: 0, count: Int(maxElement.number + 1))
for element in array {
countArray[element.number] += 1
}
// Step 2
// Set each value to be the sum of the previous two values
for index in 1 ..< countArray.count {
let sum = countArray[index] + countArray[index - 1]
countArray[index] = sum
}
print(countArray)
// Step 3
// Place the element in the final array as per the number of elements before it
let count = array.count
var sortedArray = [Foo](repeating: Foo(number: 0, name: ""), count: count)
sortedArray = array.sorted(by: { (a, b) -> Bool in
countArray[a.number] -= 1
return a.number < b.number
})
return sortedArray
}
Usage :
print(try countingSort(array: [Foo(number: 10, name: ""), Foo(number: 9, name: ""), Foo(number: 8, name: ""), Foo(number: 7, name: ""), Foo(number: 1, name: ""), Foo(number: 2, name: ""), Foo(number: 7, name: ""), Foo(number: 3, name: "")]).flatMap {$0.number})
Output
count [0, 1, 2, 3, 3, 3, 3, 5, 6, 7, 8]
sorted [1, 2, 3, 7, 7, 8, 9, 10]
The base algorithm code

Related

How to perform sorting on an array without using .sort()

I am trying to find the easiest way to sort an array without using sort() function. I tried searching but i could not find any questions that were on SWIFT. I found several questions about php and javascript and so far nothing on swift.
var arr = [7,6456,2135,164,1345,4,8,5,87456,123,2,87,724,6523,1]
var arrSorted = arr
var index = arr.count
repeat {
var previousSwapIndex = 0
for i in 1..<index {
if (arrSorted[i - 1] as! Int) > (arrSorted[i] as! Int) {
let prevVal = arrSorted[i - 1]
let currentVal = arrSorted[i]
arrSorted[i] = prevVal
arrSorted[i - 1] = currentVal
previousSwapIndex = i
}
}
index = previousSwapIndex
} while (index != 0)
print(arrSorted as Array)
This method works but i am looking for something that is better than this and easier than this.
(Edit[Clarification] :- better = faster / quicker ,as this iterates 120 times before the array is sorted)
Could someone help me out?
Here's a generic implementation of insertion sort in Swift. It takes an inout array, but you should be able to modify it to return an array if that's what you want.
func sort<T: Comparable>(_ array: inout [T]) {
var i = 1
while i < array.count {
var x = array[i]
var j = i - 1
while j >= 0 && array[j] > x {
array[j+1] = array[j]
j -= 1
}
array[j+1] = x
i += 1
}
}
To use it:
var intArr = [1, 7, 3, 6, 4]
sort(&intArr)
print(intArr) // [1, 3, 4, 6, 7]
var stringArr = ["hello", "goodbye", "a", "string", "z", "another string"]
sort(&stringArr)
print(stringArr) // ["a", "another string", "goodbye", "hello", "string", "z"]
It will work on any type that conforms to Comparable.
You can find about all the different methods of sorting from this git.
https://github.com/raywenderlich/swift-algorithm-club
I checked a few and none of them are using any .sort() functions. Pick whichever feels easier for you.
var unsortedStringArray = ["B", "C", "Z", "A", "H"]
var unsortedIntArray = [7,8,3,4,5,9,1,2,6]
func sortFunction<T:Comparable>(array: [T]) -> [T]{
var unsortedArray = array
for i in 0..<unsortedArray.count {
for j in 0..<unsortedArray.count{
var temp: T
if unsortedArray[i] < unsortedArray[j] {
temp = unsortedArray[i]
unsortedArray[i] = unsortedArray[j]
unsortedArray[j] = temp
}
}
}
return unsortedArray
}
let resultStringArray = sortFunction(array: unsortedStringArray)
let resultIntArray = sortFunction(array: unsortedIntArray)
print(resultStringArray) //["A", "B", "C", "H", "Z"]
print(resultIntArray) // [1, 2, 3, 4, 5, 6, 7, 8, 9]

Find the count of element in a sequence way in swift?

This is the array of elements:
let numbers = [7, 7, 6, 6, 4, 4, 4, 5, 5, 7]
and the output will be in the given form
[2, 2, 3, 2, 1]
I tried this method:
for item in numbers {
let trueCount = numbers.filter { $0 == item }.count
print("\(item) Total Count is \(trueCount)")
}
But it's not working.
What you are looking for is something like that:
let input = [7,7,6,6,4,4,4,5,5,7]
let reduced = input.reduce(into: [(value: Int, count: Int)]()) { acc, value in
if (acc.last?.value == value) {
acc[acc.count - 1].count += 1
} else {
acc.append((value: value, count: 1))
}
}
let counts = reduced.map { $0.count }
print(counts)
Output:
[2, 2, 3, 2, 1]

Swift 4 Conform Comparable protocol and sort method issue

I write following Struct that conform to protocol Comparable.
struct Record: Comparable {
static func < (lhs: Record, rhs: Record) -> Bool {
if lhs.wins == rhs.wins {
return lhs.losses > rhs.losses
}
return lhs.wins < rhs.wins
}
var wins: Int
var losses: Int
init(wins: Int, losses: Int) {
self.wins = wins
self.losses = losses
}
}
var a1 = Record(wins: 3, losses: 8)
var b1 = Record(wins: 3, losses: 9)
var c1 = Record(wins: 4, losses: 7)
var records = [a1, b1, c1]
records.reverse()
print(records)
It all works fine using > < == and sort too. but if I reverse sort method on array of records, It gives me output as follows:
[__lldb_expr_48.Record(wins: 4, losses: 7), __lldb_expr_48.Record(wins: 3, losses: 9), __lldb_expr_48.Record(wins: 3, losses: 8)]
Highest wins should come first followed by less wins but if wins are equal then less losses should come before more losses. Am I doing anything wrong here or missing something? I am still learning swift so might be possible.
To order an array of stuff in the reverse sort order, you can just sort by >:
records.sort(by: >)
reverse will just reverse the array, without sorting it.
You just don't sort your array before you reverse it
records.sort()
records.reverse()
There is also a universal way to sort however you want without conforming to Comparable protocol. It might be useful if you apply various sorting logic in different places or just do sorting infrequently. Try this one:
struct SimpleRecord {
var wins: Int
var losses: Int
var name: String
// we get a simple init for free
}
let a = SimpleRecord(wins: 3, losses: 8, name: "abc")
let b = SimpleRecord(wins: 3, losses: 9, name: "some ")
let c = SimpleRecord(wins: 4, losses: 7, name: "abc")
let d = SimpleRecord(wins: 3, losses: 8, name: "Abc")
var simpleRecords = [a, b, c, d]
simpleRecords.sort(by: { (lhs, rhs) -> Bool in
if lhs.wins > rhs.wins {
return true
} else if lhs.wins == rhs.wins {
if lhs.losses < rhs.losses {
return true
} else if lhs.losses == rhs.losses {
return (lhs.name.localizedStandardCompare(rhs.name) == ComparisonResult.orderedDescending)
} else {
return false
}
} else {
return false
}
})
// print(simpleRecords) // it is a lazy collection so print is a bit messy
simpleRecords.forEach{ print($0) }
Note, how we can use non-trivial (yet efficient) predefined sorting algorithms for strings (from Foundation) such as 'localizedStandardCompare', 'localizedCompare' and etc.

TwoSum Swift Solution

I just started learning coding with swift, and was trying TwoSum.
"Given an array of integers, return indices of the two numbers such that they add up to a specific target.
You may assume that each input would have exactly one solution, and you may not use the same element twice.
Example:
Given nums = [2, 7, 11, 15], target = 9,
Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1]."
I found some solutions from GitHub that I cannot understand.
code is from https://github.com/soapyigu/LeetCode-Swift/blob/master/Array/TwoSum.swift
class TwoSum {
func twoSum(_ nums: [Int], _ target: Int) -> [Int] {
var dict = [Int: Int]()
for (i, num) in nums.enumerated() {
if let lastIndex = dict[target - num] {
return [lastIndex, i]
}
dict[num] = i
}
fatalError("No valid outputs")
}
}
Could someone be so kind to explain to codes. Thanks a lot.
The dict initialised in the method stores the numbers in the input as keys, and their indices as values. The program uses this to remember which number is where. The dict can tell you things like "the number 2 is at index 0".
For each number num at index i in the input array, we subtract num from the target to find the other number that we need, in order for them to add up to target.
Now we have the other number we need, we check to see if we have seen such a number before, by searching dict. This is what the if let lastIndex = dict[target - num] part is doing. If the dict knows what index the other number is at, we return that index, and i.
If we haven't seen that number before, we record i into the dictionary under the key num, hoping that in later iterations, we can find a number that when added to num, makes 9.
Given an array of integers, return indices of the two numbers such that they add up to a specific target.
var arr:[Int] = []
func twoSum(_ nums: [Int], _ target: Int) -> [Int] {
var toggle = false
for i in 0..<nums.count {
for j in i+1..<nums.count {
if toggle == false {
if(nums[i]+nums[j]==target){
toggle = true
arr.insert(i, at: 0)
arr.insert(j, at: 1)
break
}
}
}
}
return arr
}
Example:
Given nums = [2, 7, 11, 15], target = 9,
Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].
In Sweeper's excellent answer, he explained what dict is used for: It lets you use a value from the array to find that value's index. It would be more obvious what the dictionary was used for if we called it indexes, and this code builds the same dictionary in a more explicit way:
var indexes = [Int: Int]()
for index in 0..<array.count {
let value = array[index]
indexes[value] = index
}
After that, you get a dictionary:
[2:0, 7:1, 11:2, 15:3]
You could write the function this way:
func twoSum(_ array: [Int], _ target: Int) -> [Int] {
var indexes = [Int: Int]()
for index in 0..<array.count {
let value = array[index]
indexes[value] = index
}
for index in 0..<array.count {
let value = array[index]
if let otherIndex = indexes[target - value],
index != otherIndex {
return [index, otherIndex]
}
}
fatalError("Unable to match values")
}
That is a much more long-winded (and less efficient) way of doing the same thing. It loops through the array twice instead of once, but the results should be the same.
func twoSum(array: [Int], target: Int) -> [Int] {
var dict = [Int:Int]()
for (index, number) in array.enumerated() {
let value = target - number
if let sum = dict[value] {
return [sum, index]
}
dict[number] = index
}
return [0,0]
}
/*
array=[1, 2, 3] -> target=4
enumerated() => [0,1], [1,2], [2,3]
(i, n)
v4 - 1 = 3
s[3:0]
s[3:0]
v4 - 2 = 2
s[2:0]
s[2:1]
v4 - 3 = 1
s[1:1]
s[1:2]
output [0,2]
*/
var numbers: [Int] = [1, 3, 6, 7, 7, 14, 12]
var target = 26
var result = [Int]()
for i in 0..<numbers.count {
for j in i+1..<numbers.count {
if numbers[i] + numbers[j] == target {
print(numbers[i],numbers[j])
result.append(i)
result.append(j)
}
}
}
print(Array(Set(result)))
func twoSum(_ nums: [Int], _ target: Int) -> [Int] {
var dict:[Int:Int] = [:]
for i in 0..<nums.count {
if dict[target - nums[i]] != nil {
return [dict[target - nums[i]] ?? 0, i]
} else {
dict[nums[i]] = i
}
}
return [0]
}
Here is a link to the discussion section of the TwoSum problem on Leetcode.
Lots of great Swift solutions there.
https://leetcode.com/problems/two-sum/discuss/?currentPage=1&orderBy=most_votes&query=swift.
My personal two cents -
func twoSumA(_ nums: [Int], _ target: Int) -> [Int] {
var numsHashMap: Dictionary<Int, Int> = [:]
var outputArr: [Int] = []
for index in 0..<nums.count {
let currentNum = nums[index]
if numsHashMap.keys.contains(target-currentNum) {
outputArr.append(numsHashMap[target-currentNum] ?? -1)
outputArr.append(index)
return outputArr
}
numsHashMap[currentNum] = index
}
return !outputArr.isEmpty ? outputArr : [-1, -1]
}

Flatten an Array of Arrays in Swift

Is there a counterpart in Swift to flatten in Scala, Xtend, Groovy, Ruby and co?
var aofa = [[1,2,3],[4],[5,6,7,8,9]]
aofa.flatten() // shall deliver [1,2,3,4,5,6,7,8,9]
of course i could use reduce for that but that kinda sucks
var flattened = aofa.reduce(Int[]()){
a,i in var b : Int[] = a
b.extend(i)
return b
}
Swift >= 3.0
reduce:
let numbers = [[1,2,3],[4],[5,6,7,8,9]]
let reduced = numbers.reduce([], +)
flatMap:
let numbers = [[1,2,3],[4],[5,6,7,8,9]]
let flattened = numbers.flatMap { $0 }
joined:
let numbers = [[1,2,3],[4],[5,6,7,8,9]]
let joined = Array(numbers.joined())
In Swift standard library there is joined function implemented for all types conforming to Sequence protocol (or flatten on SequenceType before Swift 3), which includes Array:
let numbers = [[1,2,3],[4],[5,6,7,8,9]]
let flattened = Array(numbers.joined())
In certain cases use of joined() can be beneficial as it returns a lazy collection instead of a new array, but can always be converted to an array when passed to Array() initialiser like in the example above.
Swift 4.x/5.x
Just to add a bit more complexity in the array, if there is an array that contains array of arrays, then flatMap will actually fail.
Suppose the array is
var array:[Any] = [1,2,[[3,4],[5,6,[7]]],8]
What flatMap or compactMap returns is:
array.compactMap({$0})
//Output
[1, 2, [[3, 4], [5, 6, [7]]], 8]
In order to solve this problem, we can use our simple for loop logic + recursion
func flattenedArray(array:[Any]) -> [Int] {
var myArray = [Int]()
for element in array {
if let element = element as? Int {
myArray.append(element)
}
if let element = element as? [Any] {
let result = flattenedArray(array: element)
for i in result {
myArray.append(i)
}
}
}
return myArray
}
So call this function with the given array
flattenedArray(array: array)
The Result is:
[1, 2, 3, 4, 5, 6, 7, 8]
This function will help to flatten any kind of array, considering the case of Int here
Playground Output:
Swift 4.x
This usage of flatMap isn't deprecated and it's make for this.
https://developer.apple.com/documentation/swift/sequence/2905332-flatmap
var aofa = [[1,2,3],[4],[5,6,7,8,9]]
aofa.flatMap { $0 } //[1,2,3,4,5,6,7,8,9]
Edit: Use joined() instead:
https://developer.apple.com/documentation/swift/sequence/2431985-joined
Original reply:
let numbers = [[1, 2, 3], [4, 5, 6]]
let flattenNumbers = numbers.reduce([], combine: +)
Swift 5.1
public extension Array where Element: Collection {
func flatten() -> [Element.Element] {
return reduce([], +)
}
}
In case you also want it for Dictionary values:
public extension Dictionary.Values where Value : Collection {
func flatten() -> [Value.Element]{
return self.reduce([], +)
}
}
Swift 4.2
I wrote a simple array extension below. You can use to flatten an array that contains another array or element. unlike joined() method.
public extension Array {
public func flatten() -> [Element] {
return Array.flatten(0, self)
}
public static func flatten<Element>(_ index: Int, _ toFlat: [Element]) -> [Element] {
guard index < toFlat.count else { return [] }
var flatten: [Element] = []
if let itemArr = toFlat[index] as? [Element] {
flatten = flatten + itemArr.flatten()
} else {
flatten.append(toFlat[index])
}
return flatten + Array.flatten(index + 1, toFlat)
}
}
usage:
let numbers: [Any] = [1, [2, "3"], 4, ["5", 6, 7], "8", [9, 10]]
numbers.flatten()
Apple Swift version 5.1.2 (swiftlang-1100.0.278 clang-1100.0.33.9)
Target: x86_64-apple-darwin19.2.0
let optionalNumbers = [[1, 2, 3, nil], nil, [4], [5, 6, 7, 8, 9]]
print(optionalNumbers.compactMap { $0 }) // [[Optional(1), Optional(2), Optional(3), nil], [Optional(4)], [Optional(5), Optional(6), Optional(7), Optional(8), Optional(9)]]
print(optionalNumbers.compactMap { $0 }.reduce([], +).map { $0 as? Int ?? nil }.compactMap{ $0 }) // [1, 2, 3, 4, 5, 6, 7, 8, 9]
print(optionalNumbers.compactMap { $0 }.flatMap { $0 }.map { $0 as? Int ?? nil }.compactMap{ $0 }) // [1, 2, 3, 4, 5, 6, 7, 8, 9]
print(Array(optionalNumbers.compactMap { $0 }.joined()).map { $0 as? Int ?? nil }.compactMap{ $0 }) // [1, 2, 3, 4, 5, 6, 7, 8, 9]
let nonOptionalNumbers = [[1, 2, 3], [4], [5, 6, 7, 8, 9]]
print(nonOptionalNumbers.compactMap { $0 }) // [[1, 2, 3], [4], [5, 6, 7, 8, 9]]
print(nonOptionalNumbers.reduce([], +)) // [1, 2, 3, 4, 5, 6, 7, 8, 9]
print(nonOptionalNumbers.flatMap { $0 }) // [1, 2, 3, 4, 5, 6, 7, 8, 9]
print(Array(nonOptionalNumbers.joined())) // [1, 2, 3, 4, 5, 6, 7, 8, 9]
You can flatten nested array using the following method:
var arrays = [1, 2, 3, 4, 5, [12, 22, 32], [[1, 2, 3], 1, 3, 4, [[[777, 888, 8999]]]]] as [Any]
func flatten(_ array: [Any]) -> [Any] {
return array.reduce([Any]()) { result, current in
switch current {
case(let arrayOfAny as [Any]):
return result + flatten(arrayOfAny)
default:
return result + [current]
}
}
}
let result = flatten(arrays)
print(result)
/// [1, 2, 3, 4, 5, 12, 22, 32, 1, 2, 3, 1, 3, 4, 777, 888, 8999]
Modified #RahmiBozdag's answer,
1. Methods in public extensions are public.
2. Removed extra method, as start index will be always zero.
3. I did not find a way to put compactMap inside for nil and optionals because inside method T is always [Any?], any suggestions are welcomed.
let array = [[[1, 2, 3], 4], 5, [6, [9], 10], 11, nil] as [Any?]
public extension Array {
func flatten<T>(_ index: Int = 0) -> [T] {
guard index < self.count else {
return []
}
var flatten: [T] = []
if let itemArr = self[index] as? [T] {
flatten += itemArr.flatten()
} else if let element = self[index] as? T {
flatten.append(element)
}
return flatten + self.flatten(index + 1)
}
}
let result: [Any] = array.flatten().compactMap { $0 }
print(result)
//[1, 2, 3, 4, 5, 6, 9, 10, 11]
flatten() was renamed to joined() in Swift 3 per SE-0133:
https://github.com/apple/swift-evolution/blob/master/proposals/0133-rename-flatten-to-joined.md
Another more generic implementation of reduce,
let numbers = [[1,2,3],[4],[5,6,7,8,9]]
let reduced = reduce(numbers,[],+)
This accomplishes the same thing but may give more insight into what is going on in reduce.
From Apple's docs,
func reduce<S : SequenceType, U>(sequence: S, initial: U, combine: (U, S.Generator.Element) -> U) -> U
Description
Return the result of repeatedly calling combine with an accumulated value initialized to initial and each element of sequence, in turn.
struct Group {
var members: [String]?
}
let groups = [Group]()
let outputMembers: [String] = Array(groups.compactMap({ $0.members }).joined())
Description
If you want to make single array of the array of object model. Ex: we get outputMembers single array from all groups.
matrix is [[myDTO]]?
In swift 5 you can use this = Array(self.matrix!.joined())
func convert(){
let arr = [[1,2,3],[4],[5,6,7,8,9]]
print("Old Arr = ",arr)
var newArr = [Int]()
for i in arr{
for j in i{
newArr.append(j)
}
}
print("New Arr = ",newArr)
}