Divide array into little arrays with the same elements - arrays

let's say I have an array this way : [1,4,7,4,2,2,4,7,1,2].
I need a function that divides this array into arrays with the same elements so it shows a result as this in swift :
result = [[1,1],[4,4,4],[7,7],[2,2,2]] .
How to do that in swift ? Thanks in advance

You can use a helper dictionary to categorize the values of your array into the appropriate bins. E.g.:
let arr = [1, 4, 7, 4, 2, 2, 4, 7, 1, 2]
var dict: [Int: [Int]] = [:]
arr.forEach { dict[$0] = (dict[$0] ?? []) + [$0] }
let inBins = dict.map{ $1 }.sorted{ $0.first ?? 0 < $1.first ?? 0 }
print(inBins) // [[1, 1], [2, 2, 2], [4, 4, 4], [7, 7]]
Or, make use of the general Sequence extension for the categorising part, as described in the accepted answer in thread linked to by #Hamish:
How to group by the elements of an array in Swift
E.g.:
/* from https://stackoverflow.com/a/39388832/4573247:
#mientus's Swift 3 translation of #oisdk's accepted answer */
public extension Sequence {
func categorise<U : Hashable>(_ key: (Iterator.Element) -> U) -> [U:[Iterator.Element]] {
var dict: [U:[Iterator.Element]] = [:]
for el in self {
let key = key(el)
if case nil = dict[key]?.append(el) { dict[key] = [el] }
}
return dict
}
}
let arr = [1, 4, 7 ,4, 2, 2, 4, 7, 1, 2]
let inBins = arr.categorise{ $0 }.map{ $1 }.sorted{ $0.first ?? 0 < $1.first ?? 0 }
print(inBins) // [[1, 1], [2, 2, 2], [4, 4, 4], [7, 7]]
No need for the bins to be sorted (as above)? The two options above are then reduced to (simply dropping the last sortin part):
// ... first alternative above
let inBins = dict.map{ $1 }
// ... 2nd alternative above
let inBins = arr.categorise{ $0 }.map{ $1 }

Another option could be to create an NSCountedSet:
let array = [1,4,7,4,2,2,4,7,1,2]
let countedSet = NSCountedSet(array: array)
You could then easily get the count of each unique element:
let countForOne = countedSet.count(for: 1)
As far as I know there is no native Swift equivalent of NSCountedSet yet.

Related

Sum an array of arrays by position resulting in one array in Swift

I have an array of arrays of Int and I want to sum every value within all the values in the same position in a performing way. For example:
let array: [[Int]] = [[1, 2, 3], [4, 5], [6, 7, 8, 9]]
The result should be:
let result: [Int] = [11, 14, 11, 9]
If this is too complex, I can make all subarrays to have the same amount of elements.
My current soultion is the following but I believe it has to be a cleaner and more efficient way of doing it:
func sumElementsInSubArrays(_ array: [[Int]]) -> [Int] {
var result: [Int] = []
for subarray in array {
for (i, value) in subarray.enumerated() {
if result.count > (i) {
result[i] = result[i] + value
} else {
result[i] = value
}
}
}
return result
}
There may be several ways like HOF to deal with this situation but if you are new to it like me , you can do it like this :
Considering all the subArrays to have same number of element :
let array = [[1, 2, 3, 4], [5, 2, 7, 0] , [1, 7, 9, 4]]
var finalArray = [Int]()
for i in 0..<(array.first?.count ?? 0) {
var aElement = 0
array.forEach { aArray in
aElement += aArray[i]
}
finalArray.append(aElement)
}
//Final array should look like this at this point : [7, 11, 19, 8]
You could use a couple of nested loops (I don't think performance wise using reduce would be faster, thought it may look better/debatably be better for readability):
func sumElementsInSubArrays(_ array: [[Int]]) -> [Int] {
var result: [Int] = []
for subarray in array {
for (i, value) in subarray.enumerated() {
if result.count > i {
result[i] += value
} else {
result.append(value)
}
}
}
return result
}
print(sumElementsInSubArrays([[1, 2, 3], [4, 5], [6, 7, 8, 9]]))
print(sumElementsInSubArrays([]))
print(sumElementsInSubArrays([[]]))
Output:
[11, 14, 11, 9]
[]
[]

How to get the summation of diagonal lines using higher-order functions?

Consider the following 2D array:
let array = [
[11, 2, 4],
[4, 5, 6],
[10, 8, -12]
]
What I want to get is the summation of the diagonals:
As firstDiagnal: 11 + 5 + (-12) = 4
As secondDiagnal: 4 + 5 + 10 = 19
I could achieve it using a standard for-in loop:
var firstDiagnal = 0
var secondDiagnal = 0
for i in 0..<array.count {
firstDiagnal += array[i][i]
secondDiagnal += array[i][array[i].count - 1 - i]
}
print(firstDiagnal)
print(secondDiagnal)
However, what could it be if we tried to use higher-order functions? such as map and reduce?
To get the first sum, you want the i'th element of the i'th row:
let firstDiag = array.enumerated().map { $1[$0] }.reduce(0, +)
To get the second sum, you want the same thing, but the columns reversed:
let secondDiag = array.enumerated().map { $1.reversed()[$0] }.reduce(0, +)
To begin with, you can write a neat extension to get the diagonal out of a nested array:
extension Array {
func diagonal<T>(order: Int) -> [T] where Element == [T] {
var order = order
return self.compactMap {
guard order >= 0, $0.count > order else {
order -= 1
return nil
}
let output = $0[order]
order -= 1
return output
}
}
}
let array = [[1, 2, 3],
[4, 5, 6],
[7, 8, 9]]
print(array.diagonal(order: 0)) // [1]
print(array.diagonal(order: 1)) // [2, 4]
print(array.diagonal(order: 2)) // [3, 5 ,7]
print(array.diagonal(order: 3)) // [6, 8]
It's then just simply a case of the bog-standard reduce and sum:
let fristDiagonal = array.diagonal(order: 0).reduce(0, +)
let secondDiagonal = array.diagonal(order: 1).reduce(0, +)

Count of Items in an Array within an Array on Swift 3

Let's say that I have some code like this:
let arr = [[1, 2], [3, 4, 5], [6, 7, 8, 9, 10]]
How would I define a function that it takes in a number i such that it goes to the ith array in arr and it prints the number of elements?
For example, let's say the function is called arrcount. Then,
arrcount(1) = 2, arrcount(2) = 3, and arrcount(3) = 5.
You don't actually need a function to achieve this:
arr[2].count // will return 5
Something along these lines should work
func countNumberOfItems(at index: Int, from arr: [Array<Any>]) -> Int? {
if index < arr.count, let temp = arr[index] as? Array {
return temp.count
} else {
return nil
}
}

Swift 3 Remove objects from an array that are present in another array using set and maintaining order [duplicate]

This question already has answers here:
iOS swift remove elements of an array from another array
(9 answers)
Closed 5 years ago.
Array1 = [1, 2, 3, 4, 5, 6]
Array2 = [1,5]
I want to get:
Array1 = [2, 3, 4, 6]
I want to do this by using Set because these arrays may get larger.
Also it is important that I maintain the order of the array.
Create a set with all elements from the second array,
then filter the first array to get only the elements which are not
in the set:
let array1 = [5, 4, 1, 2, 3, 4, 1, 2]
let array2 = [1, 5]
let set2 = Set(array2)
let result = array1.filter { !set2.contains($0) }
print(result) // [4, 2, 3, 4, 2]
This preserves the order (and duplicate elements) from the first
array. Using a set is advantageous if the second array can be large,
because the lookup is faster.
var array1 = [1, 2, 3, 4, 5, 6]
var array2 = [1,5]
var arrayResult = array1.enumerated()
.filter { !array2.contains($0.0 + 1) }
.map { $0.1 }
print(arrayResult)
[2, 3, 4, 6]
Another ways to achieve the same result:
1. User filter
let arrayResult = array1.filter { element in
return !array2.contains(element)
}
2. Use Sort
array2.sorted(by: >).forEach { if $0 < self.array1.count { self.array1.remove(at: $0) } }
Remove elements using indexes array:
Array of Strings and indexes
let animals = ["cats", "dogs", "chimps", "moose", "squarrel", "cow"]
let indexAnimals = [0, 3, 4]
let arrayRemainingAnimals = animals
.enumerated()
.filter { !indexAnimals.contains($0.offset) }
.map { $0.element }
print(arrayRemainingAnimals)
//result - ["dogs", "chimps", "cow"]
Array of Integers and indexes
var numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
let indexesToRemove = [3, 5, 8, 12]
numbers = numbers
.enumerated()
.filter { !indexesToRemove.contains($0.offset) }
.map { $0.element }
print(numbers)
//result - [0, 1, 2, 4, 6, 7, 9, 10, 11]
Remove elements using element value of another array
Arrays of integers
let arrayResult = numbers.filter { element in
return !indexesToRemove.contains(element)
}
print(arrayResult)
//result - [0, 1, 2, 4, 6, 7, 9, 10, 11]
Arrays of strings
let arrayLetters = ["a", "b", "c", "d", "e", "f", "g", "h", "i"]
let arrayRemoveLetters = ["a", "e", "g", "h"]
let arrayRemainingLetters = arrayLetters.filter {
!arrayRemoveLetters.contains($0)
}
print(arrayRemainingLetters)
//result - ["b", "c", "d", "f", "i"]
Use the filter function
let result = Array1.filter { element in
return !Array2.contains(element)
}
(Note: because you added to your question and maintaining order then my answer is not right anymore, because Set don't preserve the order. then the filter answers are a better fit)
use subtracting from a Set:
array1 = Array(Set(array1).subtracting(Set(array2)))
you can add this as an operator :
Using the Array → Set → Array method mentioned by Antonio, and with the convenience of an operator, as freytag pointed out, I've been very satisfied using this:
// Swift 3.x
func - <Element: Hashable>(lhs: [Element], rhs: [Element]) -> [Element]
{
return Array(Set<Element>(lhs).subtracting(Set<Element>(rhs)))
}
quoted from: https://stackoverflow.com/a/42679608/1930509
let array1 = [1, 2, 3, 4, 5, 6]
let array2 = [1,5]
let array3 = array1.reduce([]) { array2.contains($1) ? $0 : $0 + [$1] }
print(array3) // "[2, 3, 4, 6]\n"

Nested arrays using Any/AnyObject

According to Apple's official book "The Swift Programming Language (Swift 2.1)":
AnyObject can represent an instance of any class type.
Any can represent an instance of any type at all, including function types.
Knowing that, I wanted to emulate Python-like nested list using an Any array.
let array = [1, 2, [3, 4], [5, 6, [7, 8]], 1]
Since Int and Array are value types, I supposed that array would be typed as [Any]
But this wasn't the case here :
func flatten(list:[Any]) -> [Any] {
var new = [Any]()
for element in list {
if let array = element as? [Any] {
// this code never runs
let flattened = flatten(array)
for x in flattened {
new.append(y)
}
}else{
new.append(element)
}
}
return new
}
Note: calling this function gave me EXC_BAD_INSTRUCTION error at first until I did this tweak :
let array : [Any] = [1, 2, [3, 4], [5, 6, [7, 8]], 1]
Output : [1, 2, [3, 4], [5, 6, [7, 8]], 1]
Expected : [1, 2, 3, 4, 5, 6, 7, 8, 1]
Unexplained Solution:
I replaced everywhere in this example Any by AnyObject
Numbers now are of type NSNumber
func flatten(list:[AnyObject]) -> [AnyObject] {
var new = [AnyObject]()
for element in list {
if let array = element as? [AnyObject] {
let flattened = flatten(array)
for x in flattened {
new.append(y)
}
}else{
new.append(element)
}
}
return new
}
Output : [1, 2, 3, 4, 5, 6, 7, 8, 1]
Expected : [1, 2, 3, 4, 5, 6, 7, 8, 1]
Question :
Why is it working with [AnyObject] / NSArray and not with [Any], despite the fact that integer literals are mainly of type Int not NSNumber and array literals are of type Array and not NSArray? Is something wrong with my example?
When you use Any, Swift will wrap your nested array elements into NSArray and not into Swift arrays (which can't hold all Objective-C types). So your test if let array = element as? [Any] { will not be true if you use Any because the resulting array isn't of type [Any] but NSArray.
So if you want a function that manages Any you can change your test for:
if let nestedArray = element as? NSArray {
You then have to define a new flattenArray() function with an NSArray prototype:
func flattenArray(array: NSArray) -> [Any] {
var flattenedArray: [Any] = []
for element in array {
// We have an array
if let nestedArray = element as? NSArray {
flattenedArray = flattenedArray + flattenArray(nestedArray)
} else {
// We have a single element
flattenedArray.append(element)
}
}
return flattenedArray
}
func flattenArray(array: [Any]) -> [Any] {
var flattenedArray: [Any] = []
for element in array {
// We have an array
if let nestedArray = element as? NSArray {
flattenedArray = flattenedArray + flattenArray(nestedArray)
} else {
// We have a single element
flattenedArray.append(element)
}
}
return flattenedArray
}
and it will do the trick.
We could probably imagine a more elegant solution but with this example you get the idea behind this issue.

Resources