I want to create an array from multiple tableView selection - arrays

//this gives me an array for multiple rows selected in a tableView.
let rowsSelected = self.tableView.indexPathsForSelectedRows!.map{$0.row}
the print statement gives me a result like this for selection of row 2 and 5.....[2.5].
I now want to delete the two lines from the current list. I am struggling to find the right concept for creating the reduced array. Dictionary or working with NSIndexPath?

#vadian's answer is close, but it's dangerous; see my comment on his answer.
To fix it, you must first reverse the array of indices to be deleted. To see why, run the following lines in a playground:
let indicesToDelete = [4, 8]
var array = ["a", "b", "c", "d", "e", "f", "g", "h", "i"]
let goodReducedArray = indicesToDelete.reverse().map { array.removeAtIndex($0) }
goodReducedArray // prints ["i","e"]
let badReducedArray = indicesToDelete.map { array.removeAtIndex($0) }
badReducedArray // barfs EXC_BAD_INSTRUCTION
So the corrected version of #vadian's code will be:
if let selectedIndexPaths = self.tableView.indexPathsForSelectedRows {
selectedIndexPaths.reverse().map{tableData.removeAtIndex($0.row)}
tableView.deleteRowsAtIndexPaths(selectedIndexPaths, withRowAnimation:.Fade)
}

Assuming the name of your data source array is tableData, you can use these lines.
The items in the data source array and in the table view must be removed simultaneously, the sort and forEach functions are used to remove the items starting at the highest index.
if let selectedIndexPaths = self.tableView.indexPathsForSelectedRows {
let indexes = selectedIndexPaths.map{$0.row}.sort(>) // sort descending
indexes.forEach{tableData.removeAtIndex($0)}
tableView.deleteRowsAtIndexPaths(selectedIndexPaths, withRowAnimation:.Fade)
}

Related

Sort a list based on another list in Dart

I need to sort unsortedList based on the sorting of sortedList.
Example:
List<String> sortedList = ["x", "a", "c", "d", "w"];
// [unsortedList] items need to be also in [sortedList] and in the
// same range (eg. between 0 and 2 position)
List<String> unsortedList = ["a", "x", "c"];
You want the elements of sortedList which also appear in unsortedList, in their original order.
That's not about sorting.
No sorting is needed:
var result = sortedList.where({...unsortedList}.contains).toList();
(You can use unsortedList.contains directly, instead of creating a set, and it's probably even faster for small unsortedLists.)
Or, if your requirement is correct, and the n elements of unsortedList are the first n elements of sortedList in a different order, then maybe just var result = sortedList.sublist(0, unsortedList.length);.
If you actually care about getting the element from unsortedList, not the equal element from sortedList (which may matter if the elements are equal, but not identical), you can do:
var result = sortedList.map({...unsortedList}.lookup).whereNotNull().toList();
where whereNotNull is from package:collection.
Run the following code on DartPad
void main() async {
List<String> sortedList = ["x", "a", "c", "d", "w"];
// [unsortedList] items need to be also in [sortedList] and in the
// same range (eg. between 0 and 2 position)
List<String> unsortedList = ["a", "x", "c"];
final outcome = List<String>.generate(unsortedList.length,
(index) => unsortedList.firstWhere((item) => item == sortedList[index]));
print(outcome);
}

Extracting the first character of names in a string array into another array

I am working with a string array that has about 1100 employee names.
I want to extract the first characters of the employee names so that i can divide the names in table view alphabetically and in different sections. Just like how the contacts app on iPhone does.
i tried this for extraction
var first_char = [String]()
while (i < employeenames.count)//employeenames is the names array
{
first_char.append(String(employeenames[i].prefix(1)))
i+=1
}
This way I am getting the desired characters but the code is taking really long. Also I want to count how many times "A" or "B" shows up in first_char array. Which is again taking another many seconds and smoking the CPU.
Please someone help me with this.
You seem to want to do a "group by" operation on the array of names.
You want to group by the first character, so:
let groups = Dictionary(grouping: employeenames, by: { String($0.first!).uppercased() })
The return value will be [Character: [String]].
To count how many times A shows up, just do:
groups["A"].count
Use this code:
let employeenames = ["Peter", "John", "Frank"]
let firstChars = employeenames.map { String($0.first ?? Character("")) }
The order of the resulting single-character array will be the same as the order of the employeenames array. If you need sorting:
let sortedFirstChars = firstChars.sorted()
Given
let employee: [String] = ["James", "Tom", "Ben", "Jim"]
You can simply write
let firstCharacters = employee.compactMap { $0.first }
Result
print(firstCharacters)
["J", "T", "B", "J"]
Sorting
If you want the initial characters sorted you can add this line
let sortedFirstCharacters = firstCharacters.sorted()
["B", "J", "J", "T"]
Occurrencies
let occurrencies = NSCountedSet(array: firstCharacters)
let occurrenciesOfJ = occurrencies.count(for: Character("J"))
// 2

Faster Way to Filter?

I have an array of type "Option".
The class Optioncontains an element optionDetail.
The class optionDetail contains elements of detailTraits, which is an [String] with each string being called the detailTraitName.
So my structure of getting the detailTraits looks like Option -> optionDetail -> detailTraitswhich would return me [String], or Option -> optionDetail -> detailTraits -> detailTraitName, which would return me just one String
I would like to match up the detailTraits array with another array, named selectedDetails, which is an [String] and find the elements in which all of the selectedDetails are contained inside of the detailTraits. I then want to return all of the Option in which this situation is true.
For example, if my selectedDetails array contains ["A", "B"], and I have one detailTraits array that has ["A","C"] and one that has ["A"] and one that has ["A", "B", "C"], I just want to return the option which had detailTraits of ["A", "B", "C"]
My current code looks like the following:
newOptions = option.filter({ $0.optionDetail?.detailTraits.filter({ selectedDetails.contains($0.detailTraitName ?? "") }).count == selectedDetails.count })
Is there a better way to do this? This algorithm seems pretty inefficient since It's probably in the order of magnitude of N^3, but I can't think of a better way to look through an array of arrays and match it to another array.
Thank you!
You can optimise this by first filtering by comparing count on selectedDetails and detailTraits, and then comparing actual values. This way options set would be reduced to only those detailTraits having exact same count. For example you would only need to compare the string values with array containing exact 3 items (if selectedDetails is ["A", "B", "C"]), completely avoiding one iteration in loop.
Hope this helps

How do I remove duplicates from an array [duplicate]

This question already has answers here:
Removing duplicate elements from an array in Swift
(49 answers)
Closed 6 years ago.
Say I have an array of strings:
let arrayOfStrings = ["a", "b", "a", "c", "a", "d"]
How would I get rid of the duplicates?
You can use the array function contains(_:) to check if an element is already part of the array, but that is fairly slow, and for large arrays it won’t perform well. (1.) Better to copy the entries into a Set and use Set operations to find and remove the duplicates. Sets are optimized to make testing for set membership fast, so if aSet.contains(item) is a lot faster than if anArray.contains(item).
If you don't care about preserving the order of your items, you can simply copy your array into a set and then back to an array. However, that does mean that the items in the resulting array will be in a different order.
A function to remove duplicates from an array of strings, while preserving the order, might look like this:
func uniqueElementsFrom(array: [String]) -> [String] {
//Create an empty Set to track unique items
var set = Set<String>()
let result = array.filter {
guard !set.contains($0) else {
//If the set already contains this object, return false
//so we skip it
return false
}
//Add this item to the set since it will now be in the array
set.insert($0)
//Return true so that filtered array will contain this item.
return true
}
return result
}
If you call it with code like this:
let arrayOfStrings = ["a", "b", "a", "c", "a", "d"]
let uniqueStrings = uniqueElementsFrom(array:arrayOfStrings)
print("Unique elements from \(arrayOfStrings) = \n” +
“\(uniqueStrings)")
The output would be
Unique elements from ["a", "b", "a", "c", "a", "d"] =
[“a”, "b", "c", "d"]
However, that function only works with arrays of strings. It would be good if we could write a function that could remove duplicates from any kind of array.
This is a job for Generics. There is a catch however. Sets can only contain objects that conform to the Hashable protocol, since Sets use hashes to make testing for set membership faster.
We can rewrite the uniqueElementsFrom(array:) function to take any array that conforms to the Hashable protocol using Generics. That code looks like this:
func uniqueElementsFrom<T: Hashable>(array: [T]) -> [T] {
var set = Set<T>()
let result = array.filter {
guard !set.contains($0) else {
return false
}
set.insert($0)
return true
}
return result
}
The <T: Hashable> bit after the function name says "The rest of this function will refer to a type T which is unspecified. The only thing you can be sure of is that the type T will conform to the Hashable protocol."
This form of the uniqueElementsFrom(array:) function will work on any array who’s elements are Hashable.
(1.) For arrays, contains(_:) has O(n) performance, and so looping through an array, testing the array to see if it contains each new element with contains(_:) has performance that's almost O(n^2), which is really, really bad for anything but a small array. I'm pretty sure that Set's contains(_:) function has constant time performance, so the whole process would have O(n) performance.

Filter array based on next object

I'm writing some code to filter out driving trips from the motion sensor. I figured out the best way to do this is to add a subarray to a nested array based on the following:
Detect the first occurrence of a confident automotive event
Add all the following motion events to the same array of the events until the first confident observation that says otherwise.
For example
automotive confidence 2 //Add
automotive confidence 2 //Add
automotive confidence 2 //Add
walking confidence 2 //Add the sub-array to the master array and start over on the next confident automotive event.
Currently i'm doing it this way:
//Remove all uncertain values.
let confidentActivities = activities!.filter{$0.confidence.rawValue == 2}
var needsNew = true
var automotiveActivities:Array<Array<CMMotionActivity>> = Array() //Master array to contain subarrays of automotiveactivity arrays
var automotiveActivitySession:Array<CMMotionActivity> = Array()
for activity in confidentActivities {
if activity.automotive && (!activity.cycling && !activity.running && !activity.walking){
if needsNew {
needsNew = false
}
automotiveActivitySession.append(activity)
} else {
if !needsNew {
//If user is no longer in car, store a cpoy of the session and reset the array
automotiveActivities.append(Array(automotiveActivitySession))
automotiveActivitySession = []
needsNew = true
}
}
}
This solution is not very elegant.
Is there any way to use Swift's Array.filter{} to make this sorting prettier?
Filter is not going to do it, but you can use reduce.
The example below shows how to collect runs of consecutive "A"s (denoting an automotive event) into arrays inside an array of arrays:
let data = ["A","A","A","B","A","A","B","A","A","A","A","B","B","B","A","B","A","A","A","A","A","A","B","A"]
var res = [[String]]()
_ = data.reduce("") { (last: String, current: String) in
if current == "A" {
if last != "A" {
res.append([String]())
}
res[res.count-1].append(current)
}
return current
}
print(res)
The prior value is passed to reduce's function as the first parameter. This makes it possible for the function to decide whether to append to the current list or to start a new list.
The result of this run is as follows:
[ ["A", "A", "A"]
, ["A", "A"]
, ["A", "A", "A", "A"]
, ["A"]
, ["A", "A", "A", "A", "A", "A"]
, ["A"]]
If you're after a pretty solution, you can use split to do this. You just have to provide it with a condition on what should be considered a separator. In your case, this will be any motion event that isn't an automotive one.
let arr = ["A","A","A","B","A","A","B","A","A","C","A","B","D","B","A","B","A","E","A","A","F","A","B","A","B"]
let split = arr.split {$0 != "A"} // insert your condition for whether the given element should be considered a 'seperator'
$0 here is the anonymous closure argument for an element in the array (as it iterates through). You can always expand the closure in order to make the namings more explicit, although it looks less elegant. For example:
let split = arr.split {element in
return element != "A"
}
This will return an array of ArraySlices like so:
[
ArraySlice(["A", "A", "A"]),
ArraySlice(["A", "A"]),
ArraySlice(["A", "A"]),
ArraySlice(["A"]),
ArraySlice(["A"]),
ArraySlice(["A"]),
ArraySlice(["A", "A"]),
ArraySlice(["A"]),
ArraySlice(["A"])
]
If you want them to be explicit Arrays, you can simply use map afterwards:
let split = arr.split {$0 != "A"}.map{Array($0)}
Returns:
[
["A", "A", "A"],
["A", "A"],
["A", "A"],
["A"], ["A"],
["A"],
["A", "A"],
["A"],
["A"]
]

Resources