I have an array of numbers. I want to find those in array1 that aren't also in array2, like this:
var array1 = [1, 2, 3, 4]
var array2 = [2, 4, 5, 6]
var result = [1, 3]
I've solved the problem by looping through all the numbers in the array2 and adding them to a dictionary. Then I loop through array1 and add those that aren't in the dictionary to the result array:
var result: [Int] = []
var numbersDict: [Int : Bool] = [:]
for element in array2 {
numbersDict[element] = true
}
for element in array1 {
if numbersDict[element] == nil {
result.append(element)
}
}
I also want to find those in array2 that aren't in array1
var array1 = [1, 2, 3, 4]
var array2 = [2, 4, 5, 6]
var result = [5, 6]
I've solved this like this:
var result: [Int] = []
var numbersDict: [Int : Bool] = [:]
for element in array1 {
numbersDict[element] = true
}
for element in array2 {
if numbersDict[element] == nil {
result.append(element)
}
}
How can I do this in the most efficient way? Assuming that these arrays could potentially be tens if not hundreds of thousands of numbers long. Should I be using sorting?
Just use Set.
Example which gets elements in array1 but not in array2:
let array1: Set = [1, 2, 3, 4]
let array2: Set = [2, 4, 5, 6]
let result = array1.subtracting(array2)
print(result)
// Prints: [1, 3] <- Order may vary since it is a set
Just switch the two sets around to get the opposite result, of in array2 but not in array1.
There are lots of Set operations, another one is intersection(_:):
let result = array1.intersection(array2)
print(result)
// Prints: [2, 4] <- Again, no order
I have 5 arrays inside an array,
let arrays = [[1,2,3], [1,2,3,4], [4,5,6,7], [21, 34, 89], [555, 34]]
Now, I want to group them if they have atleast 1 similar element.
Expected output.
[[1,2,3,4,5,6,7], [21, 34, 89, 555]]
Since array 1, 2 and 3 has similar element, they will combine. And array 4 and 5 has similar element so they will be combined as well.
var finalSimilarArray = [[String]]()
let similararray = [[1,2,3,4,5,6,7], [21, 34, 89, 555]]
similarArray.forEach { array in
similarArray.enumerated().forEach { index, array2 in
if !Set(array2).intersection(Set(array)).isEmpty {
finalSimilarArray.append(contentsOf: [array] + [array2])
similarArray.remove(at: index)
}
}
}
I tried to looping and conditions with no luck, not even close. to the real deal.
Thank you, I hope I explained it clearly. :D
My 2 cent: Start with a function which merges a single array into a list of (mutually disjoint) arrays:
func mergeOne<T>(array: [T], into merged: [[T]]) -> [[T]]
where T: Hashable & Comparable
{
var set = Set(array)
var result = merged.compactMap { m -> [T]? in
if set.isDisjoint(with: m) {
return m
} else {
set.formUnion(m)
return nil
}
}
result.append(set.sorted()) // Or: result.append(Array(set))
return result
}
The array is converted to a set so that overlapping tests with another array and joining the elements can be done efficiently. This requires the elements to conform to the Hashable protocol. The Comparable conformance is only needed to sort the merged arrays – if that is not needed then this constraint can be removed.
The above function compares the given array with each element of the list, and joins them if there is an overlap. The important point is to use the new enlarged array (actually: set) for the subsequent comparisons. So arrays with no overlap with the new array are kept in the list, and the others are joined to a new array which is then appended to the list.
With that utility function we can merge a given list of arrays easily:
func merge<T>(arrays: [[T]]) -> [[T]]
where T: Hashable & Comparable
{
return arrays.reduce(into: [], { $0 = mergeOne(array: $1, into: $0) })
}
Starting with an empty list, the array elements are merged repeatedly.
Examples:
print(merge(arrays: [[1, 2, 3], [1, 2, 3, 4], [4, 5, 6, 7], [21, 34, 89], [555, 34]]))
// [[1, 2, 3, 4, 5, 6, 7], [21, 34, 89, 555]]
print(merge(arrays: [[1,2], [3, 4], [5, 6], [3, 5], [6, 2]]))
// [[1, 2, 3, 4, 5, 6]]
Compare each array element to every other array element to see if there is any overlap. If there is, then combine the arrays into a partial result. Next compare the partial result to each element in the results. If there is any overlap, then combine then. If not, then append the partial result to the array of results.
extension Collection where Element: Hashable {
func unique() -> [Element] {
return Array(Set(self))
}
}
extension Collection where Element: Equatable {
func containsAny(of other: Self) -> Bool {
for element in other {
if contains(element) {
return true
}
}
return false
}
}
func merge(arrays: [[Int]]) -> [[Int]] {
return arrays.reduce(into: Array<[Int]>(), { results, slice1 in
var partial = slice1
for slice2 in arrays {
if slice2.containsAny(of: partial) {
partial.append(contentsOf: slice2)
}
}
if let index = results.firstIndex(where: { $0.containsAny(of: partial) }) {
results[index] = (results[index] + partial).unique().sorted()
} else {
results.append(partial.unique().sorted())
}
})
}
print(merge(arrays: [[1,2,3], [1,2,3,4], [4,5,6,7], [21, 34, 89], [555, 34]]))
print(merge(arrays: [[1,2], [3, 4], [1, 3]]))
Results are:
[[1, 2, 3, 4, 5, 6, 7], [21, 34, 89, 555]]
[[1, 2, 3, 4]]
You can use a Set which guarantees unique values, when using .union which merges the values of two sets into one.
let givenArray: [Set<Int>] = [[1,2,3], [1,2,3,4], [4,5,6,7], [21, 34, 89], [555, 34]]
let expectedArray: [Set<Int>] = [[1,2,3,4,5,6,7], [21, 34, 89, 555]]
let groupedArray: [Set<Int>] = givenArray.reduce([]) { (result, numbers) -> [Set<Int>] in
var mutableResult = result
if let indexOfNumbersToMergeWith = mutableResult.firstIndex(where: { (set) -> Bool in
!set.isDisjoint(with: numbers) // Returns: `true` if the set has no elements in common with `other`
}) {
mutableResult[indexOfNumbersToMergeWith] = mutableResult[indexOfNumbersToMergeWith].union(numbers)
} else {
mutableResult.append(numbers)
}
return mutableResult
}
print(groupedArray == expectedArray) // prints: true
EDIT:
I've updated the answer to support the following given array [[1,2], [3,4], [1,3]]
let givenArray: [Set<Int>] = [[1,2], [3,4], [1,3]]
let expectedArray: [Set<Int>] = [[1,2,3,4]]
let groupedArray: [Set<Int>] = givenArray.reduce([]) { (result, numbers) -> [Set<Int>] in
var mutableResult = result
let indexesOfNumbersToMergeWith: [Int] = mutableResult.enumerated().reduce([], { (result, arguments) -> [Int] in
var mutableResult = result
let (index, numbersToCompare) = arguments
// Returns: `true` if the set has no elements in common with `other`
if !numbersToCompare.isDisjoint(with: numbers) {
mutableResult.append(index)
}
return mutableResult
})
if !indexesOfNumbersToMergeWith.isEmpty {
// Note that I've sorted the indexes in descending order. This is because everytime you remove an element, the indexes of the elements beyond is reduce by one.
let numbersToMergeWith = indexesOfNumbersToMergeWith.sorted(by: { $1 < $0 }).map { (index) -> Set<Int> in
return mutableResult.remove(at: index) // removes and returns number set
}
mutableResult.append(numbersToMergeWith.reduce(into: numbers, { $0 = $0.union($1) }))
} else {
mutableResult.append(numbers)
}
return mutableResult
}
print(groupedArray == expectedArray) // prints: true
I want to retrieve only some of the index items in a table view.
eg: I have:
let array1 = ["one","two","three","four","two","one","three"]
let array2 = ["dog","cat","lion","tiger","elephant","eagle","peacock"]
var duplicatedDict : [String:[Int]] = [:]
for (index,dateString) in array1.enumerated() {
if(duplicatedDict[dateString] == nil){
duplicatedDict[dateString] = [index]
}else{
duplicatedDict[dateString]?.append(index)
}
}
print(duplicatedDict)
//output : ["three": [2, 6], "four": [3], "one": [0, 5], "two": [1, 4]]
When I get the value "three" then I have to get only 2nd and 6th (2 and 6 is index value of the string "three" in array1) index of array2.
Output should be ("lion and peacock") in my UItableView row. How can I customise my tableview to get the above output?
The generation of duplicatedDict can be greatly simplified using the new default parameter in Swift4:
let array1 = ["one","two","three","four","two","one","three"]
let array2 = ["dog","cat","lion","tiger","elephant","eagle","peacock"]
var duplicatedDict = [String:[Int]]()
for (index, val) in array1.enumerated() {
duplicatedDict[ val, default:[] ].append(index)
}
print(duplicatedDict)
Once you have that, its easy to get the corresponding elements from array2:
if let indexes = duplicatedDict["three"] {
let animals = indexes.map { array2[$0] }
print(animals)
}
My first array is:
arr1 = [1, 2, 3, 4, 5]
My second array is:
arr2 = [2, 3, 4]
My third array is:
arr3 = ["2a", "3a", "4a"]
arr2 and arr3 are related by index values, i.e., arr2[0] relates to arr3[0], same for [1] and [2].
I have been able to compare arr2 against arr1 and add placeholder values so my placeholder array for them is
arr1to2 = ["No match", 2, 3, 4, "No Match"]
Now I need to compare arr3 against arr2 to arr1 (alternatively arr1to2), so my output should be
array_final = ["No match", 2a, 3a, 4a, "No match"]
I need to have the relationship maintained between index values of array_final and arr1to2 as well. [1] relates [1], [2] to [2], [3] to [3].
You can do it this way:
let arr1 = [1, 2, 3, 4, 5]
let arr2 = [2, 3, 4]
let arr3 = ["2a", "3a", "4a"]
// first lets join arr2 and arr3 to a dictionary
var dict: [Int: String] = [:]
zip(arr2, arr3).forEach({ (pair) in
dict[pair.0] = pair.1
})
// and then just map each value in arr1 according to the dictionary, or provide a "No match" default
let result = arr1.map { (value) -> String in
return dict[value] ?? "No match"
}
print(result)
var testarray = NSArray()
testarray = [1,2,2,3,4,5,3]
print(testarray)
testarray.removeObject(2)
I want to remove single object from multiple matching object like
myArray = [1,2,2,3,4,3]
When I remove
myArray.removeObject(2)
then both objects are deleted. I want remove only single object.
I tried to use many extension but no one is working properly. I have already used this link.
Swift 2
Solution when using a simple Swift array:
var myArray = [1, 2, 2, 3, 4, 3]
if let index = myArray.indexOf(2) {
myArray.removeAtIndex(index)
}
It works because .indexOf only returns the first occurence of the found object, as an Optional (it will be nil if object not found).
It works a bit differently if you're using NSMutableArray:
let nsarr = NSMutableArray(array: [1, 2, 2, 3, 4, 3])
let index = nsarr.indexOfObject(2)
if index < Int.max {
nsarr.removeObjectAtIndex(index)
}
Here .indexOfObject will return Int.max when failing to find an object at this index, so we check for this specific error before removing the object.
Swift 3
The syntax has changed but the idea is the same.
Array:
var myArray = [1, 2, 2, 3, 4, 3]
if let index = myArray.index(of: 2) {
myArray.remove(at: index)
}
myArray // [1, 2, 3, 4, 3]
NSMutableArray:
let myArray = NSMutableArray(array: [1, 2, 2, 3, 4, 3])
let index = myArray.index(of: 2)
if index < Int.max {
myArray.removeObject(at: index)
}
myArray // [1, 2, 3, 4, 3]
In Swift 3 we call index(of:) on both Array and NSMutableArray, but they still behave differently for different collection types, like indexOf and indexOfObject did in Swift 2.
Swift 5: getting index of the first occurrence
if let i = yourArray.firstIndex(of: yourObject) {
yourArray.remove(at: i)
}
If you want to remove all duplicate objects then you can use below code.
var testarray = NSArray()
testarray = [1,2,2,3,4,5,3]
let set = NSSet(array: testarray as [AnyObject])
print(set.allObjects)