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"]
]
Related
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);
}
How to know how many times the same Object appears in Array?
I want to check how many times I found the object, like:
array = ['A','A','A','B','B','C','C','C','D']
So, A appeared three times, B twice, C three too, and only one for D.
I know that if I use "find_all", like:
array.find_all{ |e| array.count(e) > 1 }
I will get with answer
["A", "A", "A", "B", "B", "C", "C", "C"]
but, how I can count this? I want something like:
A = 3, B = 2, C = 3, D = 1.
You can use inject on the array to iterate over the array and pass a hash into each iteration to store data. So to retrieve the count of the array you gave you would do this:
array = ["A", "A", "A", "B", "B", "C", "C", "C"]
array.inject(Hash.new(0)) do |hash, array_item|
hash[array_item] += 1
hash # this will be passed into the next iteration as the hash parameter
end
=> {"A"=>3, "B"=>2, "C"=>3}
Passing in Hash.new(0) rather than {} will mean that the default value for each key when you first encounter it will be 0.
//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)
}
I have a static list of values that is in a JSONArray. Here is my example array:
JSONArray json = new JSONArray()
json = ["B", "E", "C", "Z", "A", "X", "F", "H"]
I need to sort this json array in a custom way. I need to put "E" first, "F" second, and then sort the rest by alphabetical order.
I want my end result to be this:
json = ["E", "F", "A", "B", "C", "H", X", "Z"]
Groovy has the basic sort functionality that I can sort alphabetically or reverse alphabetically using:
json.sort()
or
json.reverse()
I'm looking for an easy way to do a custom sort.
in my 5-min experiment I used weights:
def json = ["B", "E", "C", "Z", "A", "X", "F", "H"]
def weights = [ E:10, F:9 ]
json.sort{
it.charAt( 0 ) - ( weights[ it ] ?: 0 )
}
assert '[E, F, A, B, C, H, X, Z]' == json.toString()
you might want to include some error checking
You can use closures if you define your own sort method, but what you're actually asking for is some array splitting with a little normal sorting.
json.findAll{it = 'E'} + json.findAll{it = 'F'} + json.findAll{!(it in ['E', 'F'])}.sort()
If you're worried about the efficiency of looping through your json 3 times you can iterate through your json once, adding it to different arrays as you go.
The below example is a little fancier. The inject method will iterate over a collection, passing a value between each iteration (in our case a list of 3 lists. The first list will hold our E's, the second our F's, and the 3rd for everything else. After sorting our catchall list we use .flatten() to transform the 3 lists back into one list.
List organizedList = json.inject([[],[],[]]) {List<List> result, String jsonValue ->
select(jsonValue) {
case 'E':
result.get(0).add(jsonValue) // Could have just added 'E' but I like symmetry
break;
case 'F':
result.get(1).add(jsonValue)
break;
default:
result.get(2).add(jsonValue)
}
return result // Gets passed to each iteration over json
}
organizedList.get(2).sort() // sort on a list modifies the original list
organizedList.flatten()
It's also possible using sort with a closure where you define your own sorting; but as you can see, it doesn't flow quite as easily.
json.sort {String a, String b ->
if (a = b) return 0 // For efficiency's sake
def letterFirst = {String priority -> // Closure to help sort against a hardcoded value
if (a = priority) return 1
if (b = priority) return -1
return 0
}
def toReturn = letterFirst('E')
if (!toReturn) toReturn = letterFirst('F') // groovy evaluates 0 as false
if (!toReturn) toReturn = a <=> b
return toReturn
}
What could be the swift equivalent of following python code ?
array =[ "a", "b", "c"]
print(array[1:])
( Above statement prints every element from first index upto end of array.
Output ['b', 'c'])
Edit
Is there a way where this could be done with out using array.count ? Since the array.count is redundant if I say want every element from second position
With Swift 4, there is many ways to solve your problem. According to your needs, you may choose one of the six following patterns.
#1. Using Array dropFirst() method
let array = ["a", "b", "c"]
let arraySlice = array.dropFirst()
let newArray = Array(arraySlice)
print(newArray) // prints ["b", "c"]
#2. Using Array suffix(from:) method
let array = ["a", "b", "c"]
let arraySlice = array.suffix(from: 1)
let newArray = Array(arraySlice)
print(newArray) // prints ["b", "c"]
#3. Using Array suffix(_:) method
let array = ["a", "b", "c"]
let arraySlice = array.suffix(array.endIndex.advanced(by: -1))
// let arraySlice = array.suffix(array.count - 1) // also works
let newArray = Array(arraySlice)
print(newArray) // prints ["b", "c"]
#4. Using Array subscript(_:) and CountableRange
let array = ["a", "b", "c"]
let range = array.startIndex.advanced(by: 1) ..< array.endIndex
// let range = 1 ..< array.count // also works
let arraySlice = array[range]
let newArray = Array(arraySlice)
print(newArray) // prints ["b", "c"]
#5. Using Array subscript(_:) and CountableClosedRange
let array = ["a", "b", "c"]
let range = 1 ... array.count - 1 // also works
let arraySlice = array[range]
let newArray = Array(arraySlice)
print(newArray) // prints ["b", "c"]
#6. Using Array subscript(_:) and CountablePartialRangeFrom
let array = ["a", "b", "c"]
let range = 1...
let arraySlice = array[range]
let newArray = Array(arraySlice)
print(newArray) // prints ["b", "c"]
You can get sub range of an swift array like that:
let array =[ "a", "b", "c"]
//be sure that your array.count has more than 1 item (in this case)
let subArray1 = array[1..<array.count]
print(subArray1)
//or
let subArray2 = array[1...array.count-1]
print(subArray2)
This is 2 notes from Swift Programming Language book
“Use .. to make a range that omits its upper value, and use ... to
make a range that includes both values.”
And
“If you try to use subscript syntax to retrieve or set a value for an
index that is outside of an array’s existing bounds, you will trigger
a runtime error. However, you can check that an index is valid before
using it, by comparing it to the array’s count property. Except when
count is 0 (meaning the array is empty), the largest valid index in an
array will always be count - 1, because arrays are indexed from zero.”
You can achieve what you're looking for in the following way:
1. Create a custom struct to store a start and end index. If startIndex or endIndex is nil this will be taken to mean the range extends infinitely in that direction.
struct UnboundedRange<Index> {
var startIndex, endIndex: Index?
// Providing these initialisers prevents both `startIndex` and `endIndex` being `nil`.
init(start: Index) {
self.startIndex = start
}
init(end: Index) {
self.endIndex = end
}
}
2. Define operators to create an BoundedRange as having to use the initialisers will lead to some quite unsightly code, in my option.
postfix operator ... {}
prefix operator ... {}
postfix func ... <Index> (startIndex: Index) -> UnboundedRange<Index> {
return UnboundedRange(start: startIndex)
}
prefix func ... <Index> (endIndex: Index) -> UnboundedRange<Index> {
return UnboundedRange(end: endIndex)
}
Some example usage:
1... // An UnboundedRange<Int> that extends from 1 to infinity.
...10 // An UnboundedRange<Int> that extends from minus infinity to 10.
3. Extend the CollectionType so it can handle UnboundedRanges.
extension CollectionType {
subscript(subrange: UnboundedRange<Index>) -> SubSequence {
let start = subrange.startIndex ?? self.startIndex
let end = subrange.endIndex?.advancedBy(1) ?? self.endIndex
return self[start..<end]
}
}
4. To use this in your given example:
let array = ["a", "b", "c"]
array[1...] // Returns ["b", "c"]
array[...1] // Returns ["a", "b"]