On Code.org, I'm trying to make a quiz that picks an answer based on what the user has placed in the array. However, regardless of what is in the array it always comes out false.
I've tried changing the statement to have more than one equal sign, I've tried doing it backward and making the if statement if list !== [list] and I've tried removing the quotations. Nothing has worked. I've also tried defining the correctlist variable inside the if statement, but that still produces false.
var mylist = ["a". "b", "c"];
var correctlist;
if (mylist == ["a", "b", "c"]) {
correctlist = true;
} else {
correctlist = false;
}
console.log(correctlist);
It always comes out as false.
I expected the console log to state true but it always says false.
You will notice that:["a", "b", "c"] == ["a", "b", "c"]
always returns false.
That is because these are two different arrays. Just because the elements string match, the arrays are not the same.
You will either need to iterate through the elements to compare each, convert them to something that can be compared through a plain equality, or use a library with a "deep" equals.
JSON.stringify(["a", "b", "c"]) == JSON.stringify(["a", "b", "c"])
Related
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
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.
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"]
]
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)
}