I've got 2 arrays of objects (as NSArray's) and I want to get an array of the objects in the first that aren't also in the 2nd.
See NSMutableArray's -removeObjectsInArray: method.
1) Inefficient solution. run over one array and call on each object containsObject if no do add the element otherwise take the next element
2) More efficient: sort both array and then step them side-by side. Keep a reference to the last added object and then compare the next components to this element. If none matches you can add a new "last" element.
3) Drop all the Elements in a HashTable then take the next elements of the next array and check whether you can find an "equal" object. At the end rebuild an Array from the HashTable
4) drop all the elements into an NSSet and after that rebuild an Array from this NSSet
Related
I have my document setup as:
Collection
------> Document
------> Array 1
Array 2
Array 3
I need to be able to count the number (as an Int) of arrays (or fields) in this document and be able to use it in a SwiftUI ForEach statement.
The problem is, I can't just use a specific number, because arrays are constantly being added and removed.
Since the arrays are constantly being added and removed, then you have two ways in which you can solve this. The first one would be to loop over the fields inside the document and check if the field is an array or not. Or you can simply increment/decrement a counter each time a new array is added or removed.
Basic question. What is the best way to selectively remove and delete items from a mutable Array in Swift?
There are options that do NOT seem to be suited for this like calling removeObject inside a
for in loop
enumeration block
and others that appear to work in general like
for loop using an index + calling removeObjectAtIndex, even inside the loop
for in loop for populating an arrayWithItemsToRemove and then use originalArray.removeObjectsInArray(arrayWithItemsToRemove)
using .filter to create a new array seems to be really nice, but I am not quite sure how I feel about replacing the whole original array
Is there a recommended, simple and secure way to remove items from an array? One of those I mentioned or something I am missing?
It would be nice to get different takes (with pros and cons) or preferences on this. I still struggle choosing the right one.
If you want to loop and remove elements from a NSMutableArray based on a condition, you can loop the array in reverse order (from last index to zero), and remove the objects satisfying the condition.
For example, if you have an array of integers and want to remove the numbers divisible by three, you can run the loop like this:
var array: NSMutableArray = [1, 2, 3, 4, 5, 6, 7];
for index in stride(from: array.count - 1, through: 0, by: -1) {
if array[index] as Int % 3 == 0 {
array.removeObjectAtIndex(index)
}
}
Looping in reverse order ensures that the index of the array elements still to check doesn't change. In forward mode instead, if you remove for instance the first element, then the element previously at index 1 will change to index 0, and you have to account for that in the code.
Usage of removeObject (which doesn't work with the above code) is not recommended in a loop for performance reasons, because its implementation loops through all elements of the array and uses isEqualTo to determine whether to remove the object or not. The complexity order raises from O(n) to O(n^2) - in a worst case scenario, where all elements of the array are removed, the array is traversed once in the main loop, and traversed again for each element of the array. So all solution based on enumeration blocks, for-in, etc., should be avoided, unless you have a good reason.
filter instead is a good alternative, and it's what I'd use because:
it's concise and clear: 1 line of code as opposed to 5 lines (including closing brackets) of the index based solution
its performances are comparable to the index based solution - it is a bit slower, but I think not that much
It might not be ideal in all cases though, because, as you said, it generates a new array rather than operating in place.
When working with NSMutableArray you shouldn't remove objects while you are looping along the mutable array itself (unless looping backwards, as pointed out by Antonio's answer).
A common solution is to make an immutable copy of the array, iterate on the copy, and remove objects selectively on the original mutable array by calling "removeObject" or by calling "removeObjectAtIndex", but you will have to calculate the index, since indexes in the original array and the copy will not match because of the removals (you will have to decrement the "removal index" each time an object is removed).
Another solution (better) is to loop the array once, create an NSIndexSet with the indexes of the objects to remove, and then call "removeObjectsAtIndexes:" on the mutable array.
See documentation on NSMutableArray's "removeObjectsAtIndexes:" in Swift.
Some of the options:
For loop over indexes and calling removeObjectAtIndex: 1) You will have to deal with the fact that when you remove, the index of the following object will become the current index, so you have to make sure to not increment the index in that case; you can avoid this by iterating backwards. 2) Each call to removeObjectAtIndex is O(n) (since it must shift all following elements forwards), so the algorithm is O(n^2).
For loop to build a set of elements to remove and then calling removeObjectsInArray: The first part is O(n). removeObjectsInArray uses a hash table to test elements for removal efficiently; hash table access is O(1) on average but O(n) worst-case, so the algorithm is O(n) on average, but O(n^2) worst-case.
Using filter to create a new array: This is O(n). It creates a new array.
For loop to build an index set of indexes of elements to remove (or with indexesOfObjectsPassingTest), then remove them using removeObjectsAtIndexes: I believe this is O(n). It does not create a new array.
Use filterUsingPredicate using a predicate based on a block of your test: I believe this is also O(n). It does not create a new array.
I have a problem with swift. I want to search an array of strings consists of 3 different elements filling this array in certain manner. Next i want to search the array for a subset of 3 particular strings next to each other and return their indexes. Is that a special array method for this?
Copied from comment:
Assumed i have an Int array like [1,2,1,3,2]. I want to search it for a subarray [1,2,1] and return indexes of those elements. Should i use findwithPredicate method?
I would just iterate the Array checking for matches.
Check the items in the array in sequence comparing the current item with the first item of the subarray. If it does not match move on. If it does start comparing the rest of the subarray, if it fails go bad to scanning the array.
I have two arrays
#one = ("1|1|the|dog|ran", "1|2|a|x|b", "2|8|e|s|e");
#two = ("1|2|a|x|b", "1|1|down|the|street", "2|8|e|s|e");
I need to match them by the first two "|" separated elements. So that when on $one[0] the search would return $two[1].
There are millions of lines in each array so I need the fastest way to do this.
EDIT:
Sorry for the confusion. I want to treat the first 2 "|" separated elements (ie. 1|2, 2|1) as a key for the array, loop through the first array and search the second array using that key to get the values in the second array. Does that help?
- For each record in the second array,
- Parse the record
- Add it to a hash keyed by the first two fields.
- For each record in the first array,
- Parse the record
- Look in the hash for a record with the appropriate key.
- If there is one,
- Do something with it.
I have the following 2 arrays. Array A with i.e. 10 id's, and the other array, Array B with 300 id's with all the corresponding data.
I want to retrieve all the data from B with id's which are stated in the array A.
I could just loop all entries in array B on every entry in array A, but this looks a little bit heavy for such simple task. What would be the best solutions in this case?
Thnx!
If you don't need an ordered array you could switch to an hash table (eg. NSSet) which has a much faster lookup time (strong bias towards O(1)). Otherwise you had to loop through the whole array and check all of its members.
Probably use a predicate (a, b are your arrays):
NSPredicate* filter = [NSPredicate predicateWithFormat: #"self IN %#", a];
NSArray* result = [b filteredArrayUsingPredicate: filter];
Although, it might only work if the objects in your arrays are simple things like strings.
When you are filling array B you can instead make it a dictionary. Assuming ids are unique you can then get value of the object for key in array A from array B. This will avoid looping and is sort of equivalent to hash table.
The returned value will then have the corresponding data for the particular unique id.
I don't see how you are going to do it otherwise if you are using arrays. The only way to access the right element is by looping through the array.