Find the count of element in a sequence way in swift? - 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]

Related

Find next element in matrix

I have simple two dimensional array with different number of elements in each row. My goal is find next element with elegant solution.
If we have the last element we should get the first one.
[
[1, 0, 234, 345, 678],
[123, 456, 789],
[0, 9]
]
678 -> 123
9 -> 1
If mat is a nested array then mat.joined() is a “lazy flat collection” of all the nested elements. That suggest to solve the problem for arbitrary collections:
Given a collection and an element of that collection, return the immediate successor of the element. The collection is viewed as cyclic, so that the successor of the last element is the first element.
A straight-forward implementation is (explanations inline):
extension Collection where Element: Comparable {
func cyclicNext(after elem: Element) -> Element? {
// Find (first) index of given element
guard var idx = firstIndex(of: elem) else {
return nil // Element not present in collection
}
formIndex(after: &idx) // Advance index by one
if idx == endIndex { // If past the end ...
idx = startIndex // ... then jump back to the start
}
return self[idx]
}
}
If the element occurs more than once in the collection then the successor of the first occurrence is returned.
This can be applied to your matrix:
let mat: [[Int]] = [[1, 0, 234, 345, 678],
[123, 456, 789],
[0, 9]]
print(mat.joined().cyclicNext(after: 1)) // 0
print(mat.joined().cyclicNext(after: 678)) // 123
print(mat.joined().cyclicNext(after: 123)) // 456
print(mat.joined().cyclicNext(after: 9)) // 1
print(mat.joined().cyclicNext(after: 999)) // nil
It can also be applied to other collections:
print([1, 3, 5, 7].cyclicNext(after: 3)) // 5
print([1, 3, 5, 7].cyclicNext(after: 7)) // 1
print("Hello World".cyclicNext(after: "W")) // "o"
print("Hello World".cyclicNext(after: "d")) // "H"
I believe this does do the job :
extension Array where Element == [Int] {
func element(after x: Int) -> Int? {
var arrayIndex = 0
while arrayIndex < self.count {
//If an array contains the searched element
if let xIndex = self[arrayIndex].firstIndex(where: { $0 == x }) {
//if the next element is in the same array
if xIndex < self[arrayIndex].count - 1 {
return self[arrayIndex][xIndex + 1]
}
//if the next element is in the next array
else if arrayIndex < self.count - 1 {
return self[arrayIndex + 1][0]
}
//if the x is the last element in the last array
else {
return self[0][0]
}
}
arrayIndex += 1
}
return nil
}
}
And here are some test cases :
let mat = [
[1, 0, 234, 345, 678],
[123, 456, 789],
[0, 9]
]
mat.element(after: 678) //123
mat.element(after: 9) //1
mat.element(after: 1) //0
mat.element(after: 0) //234
mat.element(after: 3) //nil
A possible solution is to flatten your array.
Tested on Playground:
let mat: [[Int]] = [[1, 0, 234, 345, 678],
[123, 456, 789],
[0, 9]]
func next(element: Int, in matrix: [[Int]]) -> Int? {
let flatten = matrix.flatMap({ $0 })
guard let index = flatten.firstIndex(of: element) else {
print("Element \(element) not found in matrix")
return nil
}
if index == flatten.endIndex - 1 {
return flatten.first
} else {
return flatten[index + 1 ]
}
}
let result1 = next(element: 678, in: mat)
print("End of subarray Test: \(result1)")
let result2 = next(element: 9, in: mat)
print("Last one Test: \(result2)")
let result3 = next(element: 66, in: mat)
print("Not present test: \(result3)")
let result0 = next(element: 1, in: mat)
print("First one test: \(result0)")
Output:
$>End of subarray Test: Optional(123)
$>Last one Test: Optional(1)
$>Element 66 not found in matrix
$>Not present test: nil
$>First one test: Optional(0)
I don't know if it's enough elegant for you.
One optimization would have to keep the flatten and not recalculate each time.

Implementing counting sort with custom objects

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

How to create lazy combinations

My question is very simple, how do I make this code lazy:
/*
input: [
[1, 2],
[3, 4],
[5, 6]
]
output: [
[1, 3, 5],
[1, 3, 6],
[1, 4, 5],
[1, 4, 6],
[2, 3, 5],
[2, 3, 6],
[2, 4, 5],
[2, 4, 6],
]
*/
func combinations<T>(options: [[T]]) -> [[T]] {
guard let head = options.first else {
return [].map({ [$0] })
}
if options.count == 1 {
return head.map({ [$0] })
}
let tailCombinations = combinations(options: Array(options.dropFirst()))
return head.flatMap({ option in
return tailCombinations.map({ combination -> [T] in
return [option] + combination
})
})
}
The code above works to calculate the combinations, but it does so creating the entire array of arrays in memory.
What I need is to have it return something like LazySequence<Array<T>>, except the Swift type system doesn't let me do something that generic.
Any ideas how to achieve this and keep the functional style?
Ps.: I did think of another way to solve this problem with generators and keeping track of indexes, but I don't wanna keep track of any state, I want a pure functional (as in FP) solution.
Haskell does it by default, btw, and I'm looking for the same thing.
EDIT: I've managed to solve part of the problem, the type system, with AnyCollection
func combinations<T>(options: [[T]]) -> LazyCollection<AnyCollection<[T]>> {
guard let head = options.first else {
return AnyCollection([].lazy.map({ [$0] })).lazy
}
if options.count == 1 {
return AnyCollection(head.lazy.map({ [$0] })).lazy
}
let tailCombinations = combinations(options: Array(options.dropFirst()))
return AnyCollection(head.lazy.flatMap({ option in
return tailCombinations.lazy.map({ [option] + $0 })
})).lazy
}
But when I use the function, it loads the entire collection in memory, i.e., not lazy.
EDIT 2: Doing some more investigation, turns out the problem is with AnyCollection
// stays lazy
let x1 = head.lazy.flatMap({ option in
return tailCombinations.lazy.map({ [option] + $0 })
})
// forces to load in memory
let x2 = AnyCollection(head.lazy.flatMap({ option in
return tailCombinations.lazy.map({ [option] + $0 })
}))
Not sure how to solve this yet.
Here is what I came up with:
func combinations<T>(options: [[T]]) -> AnySequence<[T]> {
guard let lastOption = options.last else {
return AnySequence(CollectionOfOne([]))
}
let headCombinations = combinations(options: Array(options.dropLast()))
return AnySequence(headCombinations.lazy.flatMap { head in
lastOption.lazy.map { head + [$0] }
})
}
The main difference to this solution is that the recursive
call creates a sequence
of the first N-1 options, and then combines each element of
that sequence with each element of the last option. This is more
efficient because the sequence returned from the recursive call
is enumerated only once, and not once for each element that it is
combined with.
Other differences are:
There is no need to call .lazy on the AnySequence if that
sequence is already lazy. The return type is therefore "simplified"
to AnySequence<[T]>.
I have used CollectionOfOne to create a single-element sequence
for the empty array.
Treating the case options.count == 1 separately is not necessary
for the algorithm to work (but might be a possible performance
improvement).
A completely different approach is to define a custom collection type
which computes each combination as a function of the index, using
simple modulo arithmetic:
struct Combinations<T> : RandomAccessCollection {
let options: [[T]]
let startIndex = 0
let endIndex: Int
init(options: [[T]]) {
self.options = options.reversed()
self.endIndex = options.reduce(1) { $0 * $1.count }
}
subscript(index: Int) -> [T] {
var i = index
var combination: [T] = []
combination.reserveCapacity(options.count)
options.forEach { option in
combination.append(option[i % option.count])
i /= option.count
}
return combination.reversed()
}
}
No extra storage is needed and no recursion. Example usage:
let all = Combinations(options: [[1, 2], [3, 4], [5, 6]])
print(all.count)
for c in all { print(c) }
Output:
8
[1, 3, 5]
[1, 3, 6]
[1, 4, 5]
[1, 4, 6]
[2, 3, 5]
[2, 3, 6]
[2, 4, 5]
[2, 4, 6]
Testing with
let options = Array(repeating: [1, 2, 3, 4, 5], count: 5)
this collection-based method turned out to be faster then the
my above sequence-based method by a factor of 2.
I found one possible solution, but I'll leave this answer not accepted for a while to see if someone knows a better one.
func combinations<T>(options: [[T]]) -> LazySequence<AnySequence<[T]>> {
guard let head = options.first else {
return AnySequence([].lazy.map({ [$0] })).lazy
}
if options.count == 1 {
return AnySequence(head.lazy.map({ [$0] })).lazy
}
let tailCombinations = combinations(options: Array(options.dropFirst()))
return AnySequence(head.lazy.flatMap({ option in
return tailCombinations.lazy.map({ [option] + $0 })
})).lazy
}
The solution was to use AnySequence instead of AnyCollection.
I'm not sure why though, I'd still like to have the AnyCollection interface rather than AnySequence, since it provides me with a few more methods, like count.

Unwrapping creates barrier for Value types

Looking at the following code (playground ready).
I'm unable to append data to an unwrapped optional array from within an if let statement.
What's the best method to allow appending for an optional array?
class ValueTypeTest {
var data: [Int]?
func tryAppend() {
if var unwrappedData = self.data {
unwrappedData += [2, 3] // problem !
self.data! += [4, 5] // works
} else {
// Initialize paging and initial results
self.data = [0, 1]
}
}
}
var v = ValueTypeTest()
v.data // nil
v.tryAppend()
v.data // [0, 1]
v.tryAppend()
v.data // expected: [0, 1, 2, 3, 4, 5] actual: [0, 1, 4, 5]
Because Array is a value type and will be copied when you do the if var operation. So you're not changing self.data but unwrappedData.
Try this:
self.data = (self.data ?? [0, 1]) + [2, 3, 4, 5]
This is what I mean.
func tryAppend() {
if let unwrappedData = self.data {
self.data = unwrappedData + [2, 3, 4, 5]
} else {
// Initialize paging and initial results
self.data = [0, 1]
}
}
var unwrappedData declared inside the function tryAppend() is a local variable for that function.
The result pane at that line shows you the output containing: [0, 1, 2, 3] for unwrappedData.

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)
}