Checking if any element in one array exists in another - arrays

I am trying to create a filtering system for some items by checking if the selected items exist in the presented values.
So my selectable array can range from 0 to 6, and each item contains an array of Ints that it is associated with:
let items = [
Item(cat: [1, 2, 3]),
Item(cat: [0, 6]),
Item(cat: []),
Item(cat: [0, 1])
]
I wanted to create a function that would check if any of the cat values were in the selected Ints:
#Published var filteredCats: [Int] = []
func filterByInt(array: [Item]) -> [Item] {
let output = array.filter({
guard let cats = $0.cats else { return true }
for cat in cats {
return filteredCats.contains(cat)
}
})
return output
}
But I'm having issue with the above since it returns in the loop on the first iteration, so if I was searching for 1 in the above items then Item(cat: [0, 1]) exits at false as the first looped check is 0==1.
Essentially I want to be able to do the following (in expanded terms):
let filter = [0, 3, 4]
let items = [
[1, 2, 3],
[2, 3],
[],
[5, 6, 7]
]
items.contains(filter) // --> return the arrays
Sorry if this is basic but I've been trying to find a good solution.

Checking uniqueness is where a Set can help
struct Item {
var cat: Set<Int>
}
let items = [
Item(cat: [1, 2, 3]),
Item(cat: [0, 6]),
Item(cat: []),
Item(cat: [0, 1])
]
let wanted: Set<Int> = [0, 3, 4]
let filtered = items.filter { !$0.cat.isDisjoint(with: wanted) }

I'd suggest someSatisfy (or any in Kotlin):
We can utilize allSatisfy with double negations (one for allSatisfy, the other for contains) for that:
struct Item: CustomDebugStringConvertible {
let cat: [Int]
var debugDescription: String {
"[" + cat.map { String($0) }.joined(separator: ", ") + "]"
}
}
func filterByInt(items: [Item], filter: [Int]) -> [Item] {
// Early return might make sense if the filter is empty.
// guard !filter.isEmpty else { return items }
items.filter { item in
!filter.allSatisfy {
filterItem in !item.cat.contains(filterItem)
}
}
}
let filter = [0, 3, 4]
let items: [Item] = [
.init(cat: [1, 2, 3]),
.init(cat: [2, 3]),
.init(cat: []),
.init(cat: [5, 6, 7])
]
print(filterByInt(items: items, filter: filter))
// [[1, 2, 3], [2, 3]]
print(filterByInt(items: items, filter: [5]))
// [[5, 6, 7]]
print(filterByInt(items: items, filter: []))
// []

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]
[]
[]

Filter an object of objects by key

I'm having an object like this
{
"GroupA": {
"Parent1": [1, 2, 3],
"Parent2": [1, 2, 3],
"Parent12": [1, 2, 3]
},
"GroupB": {
"Parent13": [1, 2, 3],
"Parent5": [1, 2, 3]
},
"GroupC": {
"Parent7": [1, 2, 3]
}
}
Now i want to filter this object by searching the name of the Parent
For example when I search parent1 the result should be
{
"GroupA": {
"Parent1": [1, 2, 3],
"Parent12": [1, 2, 3]
},
"GroupB": {
"Parent13": [1, 2, 3],
}
}
Here is my solution but it's not working correctly if a Group has many similar Parent name it only return the first one. And when I try to set state it filter like all wrong value
let newData = []
let catalogArr = Object.entries(catalogList)
const handleSearchElement = (e) => {
const value = e.target.value
catalogArr.forEach(catalog => {
let filteredKeys = Object.keys(catalog[1]).filter(name => name.toLowerCase().includes(value.toLowerCase()))
let valuesOfKey
if(filteredKeys[0]) {
valuesOfKey = {
[filteredKeys[0]]: Object.values(catalog[1][filteredKeys[0]])
}
newData.push([catalog[0], {...valuesOfKey}])
}
})
console.log(Object.fromEntries(newData));
setCatalogList(Object.fromEntries(newData))
// console.log(catalogList);
}
You can use Array#reduce to accomplish this pretty easily, however, all of the packing and unpacking of objects using Object.entries and Object.fromEntries to essentially treat them as arrays suggests you may be using the wrong data structure.
If you need to do this repeatedly, look into doing a one-off transformation that arranges the data for O(1) access, for example, by grouping on inner keys rather than outer keys (hard to say since I don't know the data or use case). Or, if you're mostly iterating, consider using arrays.
const data = {
"GroupA": {
"Parent1": [1, 2, 3],
"Parent2": [1, 2, 3],
"Parent12": [1, 2, 3]
},
"GroupB": {
"Parent13": [1, 2, 3],
"Parent5": [1, 2, 3]
},
"GroupC": {
"Parent7": [1, 2, 3]
}
};
const targetKey = "parent1";
const res = Object.entries(data).reduce((a, [k, v]) => {
const filtered = Object.entries(v).filter(([k, ]) =>
k.toLowerCase().includes(targetKey.toLowerCase())
);
if (filtered.length) {
a[k] = Object.fromEntries(filtered);
}
return a;
}, {});
console.log(res);

Get all possible combination of items in array without duplicate groups in Swift

I'm trying to create an extension on Array where I can get all possible combinations of an array without generating duplicate groups, including a no item combination.
For example, for this array:
[1, 2, 3, 4]
The following possible combinations should be generated:
[[], [1], [2], [3], [4], [1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4], [1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4], [1, 2, 3, 4]]
Please note that none of the groups repeat themselves, i.e: if there is a group [1, 2] there is no other group: [2, 1].
This is the closest I've been able to get to a result:
public extension Array {
func allPossibleCombinations() -> [[Element]] {
var output: [[Element]] = [[]]
for groupSize in 1...self.count {
for (index1, item1) in self.enumerated() {
var group = [item1]
for (index2, item2) in self.enumerated() {
if group.count < groupSize {
if index2 > index1 {
group.append(item2)
if group.count == groupSize {
output.append(group)
group = [item1]
continue
}
}
} else {
break
}
}
if group.count == groupSize {
output.append(group)
}
}
}
return output
}
}
But it is missing possible combination of items in the group size 3 (I only get back [1, 2, 3] and [2, 3, 4].
Much appreciated!
You can use flatMap also to combine them in one line.
extension Array {
var combinationsWithoutRepetition: [[Element]] {
guard !isEmpty else { return [[]] }
return Array(self[1...]).combinationsWithoutRepetition.flatMap { [$0, [self[0]] + $0] }
}
}
print([1,2,3,4].combinationsWithoutRepetition)
extension Array {
var combinations: [[Element]] {
if count == 0 {
return [self]
}
else {
let tail = Array(self[1..<endIndex])
let head = self[0]
let first = tail.combinations
let rest = first.map { $0 + [head] }
return first + rest
}
}
}
print([1, 2, 3, 4].combinations)
This is in Algorithms now.
import Algorithms
Array([1, 2, 3, 4].combinations(ofCount: 0...))

Is it possible to remove duplicate values from Array with nil values

Array can be any type like
let myArray1 = [ 1, 2, 3, 1, 2, 1, 3, nil, 1, nil]
let myArray2 = [ 1, 2.0, 1, 3, 1.0, nil]
After removing duplicate values from the array, the new array should be:
Output -
[ 1, 2, 3, nil ]
#Daniel's solution as a generic function:
func uniqueElements<T: Equatable>(of array: [T?]) -> [T?] {
return array.reduce([T?]()) { (result, item) -> [T?] in
if result.contains(where: {$0 == item}) {
return result
}
return result + [item]
}
}
let array = [1,2,3,1,2,1,3,nil,1,nil]
let r = uniqueElements(of: array) // [1,2,3,nil]
You can use this reduce to remove duplicated entries:
myArray.reduce([Int?]()) { (result, item) -> [Int?] in
if result.contains(where: {$0 == item}) {
return result
}
return result + [item]
}
output: [1, 2, 3, nil]
Please check this code, I have used NSArray after getting filtered array you can convert into swift array
let arr = [1, 1, 1, 2, 2, 3, 4, 5, 6, nil, nil, 8]
let filterSet = NSSet(array: arr as NSArray as! [NSObject])
let filterArray = filterSet.allObjects as NSArray //NSArray
print("Filter Array:\(filterArray)")

Divide array into little arrays with the same elements

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.

Resources