Searching in dictionary - swift

I have a simple dictionary:
struct AnnotationDict: Encodable, Decodable {
let id: Int
let index: Int
}
var sortedAnnotation: [AnnotationDict] = []
let ADict = AnnotationDict(id: 1, index: 2)
sortedAnnotation.append(ADict)
How get index from AnnotationDict searching by id=1 ?

Like the others have said, you don't have a dictionary, you have an array.
let item = sortedAnnotation.first { $0.id == 1 } returns the first item whose id == 1. Note that the type of the returned item is optional i.e. AnnotationDict?.
To get its index, you do item!.index, though force-unwrapping is not recommended.

You don't have a dictionary. You have an array of Structs.
Larme gave you the answer:
let itemWithID1 = sortedAnnotation.first(where: { $0.id == 1})
or more generally
func itemWithIndex(_ index: Int, inAnnotationDictArray annotationDictArray: [AnnotationDict]) -> AnnotationDict? {
return annotationDictArray.first(where: { $0.id == index})
}
Note that first(where:) operates in O(n) time, so it will be slow, and expressions that run in O(n^2) time are easy to create without realizing it. (For those without a formal CS background, that means "The time to complete goes up with the square of the number of elements, so it's easy to write code that gets REALLY slow for more than a very small number of items.") Finding items in a real Dictionary takes ≈constant time, which is much better.

Related

How can I compare two Arrays in Swift and mutate one of the arrays if two Items are the same

I want to compare two Arrays with each other, that means each single item of them.
I need to run some code if two items in this arrays are the same.
I've done that so far with two For-Loops, but someone in the comments says that's not that good (because I get an Error too.
Has anybody an Idea which Code I can use to reach that?
Btw: That's the code I used before:
var index = 0
for Item1 in Array1 {
for Item2 in Array2 {
if (Item1 == Item2) {
// Here I want to put some code if the items are the same
}
}
// Index is in this case the counter which counts on which place in the Array1 I am.
index += 1
}
Okay I'll try again to describe what I mean:
I want to compare two Arrays. If there are some Items the same, I want to delete that Item from Array A / Array 1.
Okay if that is working, I want to add a few more statements that alow me to delete a Item only if an parameter of this item has a special worth, but I think I can do this step alone.
If you want to compare items from different array you need to add Equatable protocol for your Item
For example:
struct Item: Equatable {
let name: String
static func ==(l: Item, r: Item) -> Bool {
return l.name == r.name
}
}
You need to decide by which attributes you want to compare your Item. In my case I compare by name.
let array1: [Item] = [
.init(name: "John"),
.init(name: "Jack"),
.init(name: "Soer"),
.init(name: "Kate")
]
let array2: [Item] = [
.init(name: "John"),
]
for item1 in array1 {
if array2.contains(item1) {
// Array 2 contains item from the array1 and you can perform your code.
}
}
If you want to support this
Okay I'll try again to describe what I mean: I want to compare two
Arrays. If there are some Items the same, I want to delete that Item
from Array A / Array 1. Okay if that is working, I want to add a few
more statements that alow me to delete a Item only if an parameter of
this item has a special worth, but I think I can do this step alone.
I guess it can fit for you
You need to make your array1 mutable
var array1: [Item] = ...
Filter the array1 like this
let filteredArray1 = array1.filter { (item) -> Bool in
return !array2.contains(item)
}
And redefine your array1 with filtered array.
array1 = filteredArray1
Let me know it it works for you.
var itemsToRemove = array1.filter { array2.contains($0) }
for item in itemsToRemove {
if let i = array1.firstIndex(of: item) {
// run your conditional code here.
array1.remove(at: i)
}
}
Update
The question has been restated that elements are to be removed from one of the arrays.
You don't provide the actual code where you get the index out of bounds error, but it's almost certainly because you don't account for having removed elements when using the index, so you're probably indexing into a region that was valid at the start, but isn't anymore.
My first advice is don't do the actual removal inside the search loop. There is a solution to achieve the same result, which I'll give, but apart from having to be very careful about indexing into the shortened array, there is also a performance issue: Every deletion requires Array to shift all the later elements down one. That means that every deletion is an O(n) operation, which makes an the overall algorithim O(n^3).
So what do you do instead? The simplest method is to create a new array containing only the elements you wish to keep. For example, let's say you want to remove from array1 all elements that are also in array2:
array1 = array1.filter { !array2.contains($0) }
I should note that one of the reasons I kept my original answer below is because you can use those methods to replace array2.contains($0) to achieve better performance in some cases, and the original answer describes those cases and how to achieve it.
Also even though the closure is used to determine whether or not to keep the element, so it has to return a Bool, there is nothing that prevents you from putting additional code in it to do any other work you might want to:
array1 = array1.filter
{
if array2.contains($0)
{
doSomething(with: $0)
return false // don't keep this element
}
return true
}
The same applies to all of the closures below.
You could just use the removeAll(where:) method of Array.
array1.removeAll { array2.contains($0) }
In this case, if the closure returns true, it means "remove this element" which is the opposite sense of the closure used in filter, so you have to take that into account if you do additional work in it.
I haven't looked up how the Swift library implements removeAll(where:) but I'm assuming it does its job the efficient way rather than the naive way. If you find the performance isn't all that good you could roll your own version.
extension Array where Element: Equatable
{
mutating func efficientRemoveAll(where condition: (Element) -> Bool)
{
guard count > 0 else { return }
var i = startIndex
var iEnd = endIndex
while i < iEnd
{
if condition(self[i])
{
iEnd -= 1
swapAt(i, iEnd)
continue // note: skips incrementing i,iEnd has been decremented
}
i += 1
}
removeLast(endIndex - iEnd)
}
}
array1.efficientRemoveAll { array2.contains($0) }
Instead of actually removing the elements inside the loop, this works by swapping them with the end element, and handling when to increment appropriately. This collects the elements to be removed at the end, where they can be removed in one go after the loop finishes. So the deletion is just one O(n) pass at the end, which avoids increasing the algorithmic complexity that removing inside the loop would entail.
Because of the swapping with the current "end" element, this method doesn't preserve the relative order of the elements in the mutating array.
If you want to preserve the order you can do it like this:
extension Array where Element: Equatable
{
mutating func orderPreservingRemoveAll(where condition: (Element) -> Bool)
{
guard count > 0 else { return }
var i = startIndex
var j = i
repeat
{
swapAt(i, j)
if !condition(self[i]) { i += 1 }
j += 1
} while j != endIndex
removeLast(endIndex - i)
}
}
array1.orderPreservingRemoveAll { array2.contains($0) }
If I had to make a bet, this would be very close to how standard Swift's removeAll(where:) for Array is implemented. It keeps two indices, one for the current last element to be kept, i, and one for the next element to be examined, j. This has the effect of accumulating elements to be removed at the end (those past i).
Original answer
The previous solutions are fine for small (yet still surprisingly large) arrays, but they are O(n^2) solutions.
If you're not mutating the arrays, they can be expressed more succinctly
array1.filter { array2.contains($0) }.forEach { doSomething($0) }
where doSomething doesn't actually have to be a function call - just do whatever you want to do with $0 (the common element).
You'll normally get better performance by putting the smaller array inside the filter.
Special cases for sorted arrays
If one of your arrays is sorted, then you might get better performance in a binary search instead of contains, though that will require that your elements conform to Comparable. There isn't a binary search in the Swift Standard Library, and I also couldn't find one in the new swift-algorithms package, so you'd need to implement it yourself:
extension RandomAccessCollection where Element: Comparable, Index == Int
{
func sortedContains(_ element: Element) -> Bool
{
var range = self[...]
while range.count > 0
{
let midPoint = (range.startIndex + range.endIndex) / 2
if range[midPoint] == element { return true }
range = range[midPoint] > element
? self[..<midPoint]
: self[index(after: midPoint)...]
}
return false
}
}
unsortedArray.filter { sortedArray.sortedContains($0) }.forEach { doSomething($0) }
This will give O(n log n) performance. However, you should test it for your actual use case if you really need performance, because binary search is not especially friendly for the CPU's branch predictor, and it doesn't take that many mispredictions to result in slower performance than just doing a linear search.
If both arrays are sorted, you can get even better performance by exploiting that, though again you have to implement the algorithm because it's not supplied by the Swift Standard Library.
extension Array where Element: Comparable
{
// Assumes no duplicates
func sortedIntersection(_ sortedOther: Self) -> Self
{
guard self.count > 0, sortedOther.count > 0 else { return [] }
var common = Self()
common.reserveCapacity(Swift.min(count, sortedOther.count))
var i = self.startIndex
var j = sortedOther.startIndex
var selfValue = self[i]
var otherValue = sortedOther[j]
while true
{
if selfValue == otherValue
{
common.append(selfValue)
i += 1
j += 1
if i == self.endIndex || j == sortedOther.endIndex { break }
selfValue = self[i]
otherValue = sortedOther[j]
continue
}
if selfValue < otherValue
{
i += 1
if i == self.endIndex { break }
selfValue = self[i]
continue
}
j += 1
if j == sortedOther.endIndex { break }
otherValue = sortedOther[j]
}
return common
}
}
array1.sortedIntersection(array2).forEach { doSomething($0) }
This is O(n) and is the most efficient of any of the solutions I'm including, because it makes exactly one pass through both arrays. That's only possible because both of the arrays are sorted.
Special case for large array of Hashable elements
However, if your arrays meet some criteria, you can get O(n) performance by going through Set. Specifically if
The arrays are large
The array elements conform to Hashable
You're not mutating the arrays
Make the larger of the two arrays a Set:
let largeSet = Set(largeArray)
smallArray.filter { largeSet.contains($0) }.forEach { doSomething($0) }
For the sake of analysis if we assume both arrays have n elements, the initializer for the Set will be O(n). The intersection will involve testing each element of the smallArray's elements for membership in largeSet. Each one of those tests is O(1) because Swift implements Set as a hash table. There will be n of those tests, so that's O(n). Then the worst case for forEach will be O(n). So O(n) overall.
There is, of course, overhead in creating the Set, the worst of which is the memory allocation involved, so it's not worth it for small arrays.

Swift, random word from let by pressing Button [duplicate]

As of Swift 1.2, Apple introduces Set collection type.
Say, I have a set like:
var set = Set<Int>(arrayLiteral: 1, 2, 3, 4, 5)
Now I want to get a random element out of it. Question is how? Set does not provide subscript(Int) like Array does. Instead it has subscript(SetIndex<T>). But firstly, SetIndex<T> does not have accessible initializers (hence, I can not just create an index with the offset I need), and secondly even if I can get the index for a first element in a set (var startIndex = set.startIndex) then the only way I can get to the N-th index is through consecutive calls to successor().
Therefore, I can see only 2 options at the moment, both ugly and expensive:
Convert the set into array (var array = [Int](set)) and use its subscript (which perfectly accepts Int); or
Get index of a first element in a set, traverse the chain of successor() methods to get to the N-th index, and then read corresponding element via set's subscript.
Do I miss some other way?
Starting with Swift 4.2, you can use randomElement:
let random = set.randomElement()
Probably the best approach is advance which walks successor for you:
func randomElementIndex<T>(s: Set<T>) -> T {
let n = Int(arc4random_uniform(UInt32(s.count)))
let i = advance(s.startIndex, n)
return s[i]
}
(EDIT: Heh; noticed you actually updated the question to include this answer before I added it to my answer... well, still a good idea and I learned something too. :D)
You can also walk the set rather than the indices (this was my first thought, but then I remembered advance).
func randomElement<T>(s: Set<T>) -> T {
let n = Int(arc4random_uniform(UInt32(s.count)))
for (i, e) in enumerate(s) {
if i == n { return e }
}
fatalError("The above loop must succeed")
}
In swift 3
extension Set {
public func randomObject() -> Element? {
let n = Int(arc4random_uniform(UInt32(self.count)))
let index = self.index(self.startIndex, offsetBy: n)
return self.count > 0 ? self[index] : nil
}
}
extension Set {
func randomElement() -> Element? {
return count == 0 ? nil : self[advance(self.startIndex, Int(arc4random()) % count)]
}
}
As per comments above re Swift updates, used a minor change for an extension to Set:
func randomElement() -> Element?
{
let randomInt = Int(arc4random_uniform(UInt32(self.count)))
let index = startIndex.advancedBy(randomInt)
return count == 0 ? nil: self[index]
}
If you want a 'random' element from a Set then you use:
/// A member of the set, or `nil` if the set is empty.
var first: T? { get }
Get the 0th index or the 1,000,000th index makes no difference - they are all an arbitrary object.
But, if you want repeated calls to return a likely different element each time, then first might not fit the bill.

How to handle initial nil value for reduce functions

I would like to learn and use more functional programming in Swift. So, I've been trying various things in playground. I don't understand Reduce, though. The basic textbook examples work, but I can't get my head around this problem.
I have an array of strings called "toDoItems". I would like to get the longest string in this array. What is the best practice for handling the initial nil value in such cases? I think this probably happens often. I thought of writing a custom function and use it.
func optionalMax(maxSofar: Int?, newElement: Int) -> Int {
if let definiteMaxSofar = maxSofar {
return max(definiteMaxSofar, newElement)
}
return newElement
}
// Just testing - nums is an array of Ints. Works.
var maxValueOfInts = nums.reduce(0) { optionalMax($0, $1) }
// ERROR: cannot invoke 'reduce' with an argument list of type ‘(nil, (_,_)->_)'
var longestOfStrings = toDoItems.reduce(nil) { optionalMax(count($0), count($1)) }
It might just be that Swift does not automatically infer the type of your initial value. Try making it clear by explicitly declaring it:
var longestOfStrings = toDoItems.reduce(nil as Int?) { optionalMax($0, count($1)) }
By the way notice that I do not count on $0 (your accumulator) since it is not a String but an optional Int Int?
Generally to avoid confusion reading the code later, I explicitly label the accumulator as a and the element coming in from the serie as x:
var longestOfStrings = toDoItems.reduce(nil as Int?) { a, x in optionalMax(a, count(x)) }
This way should be clearer than $0 and $1 in code when the accumulator or the single element are used.
Hope this helps
Initialise it with an empty string "" rather than nil. Or you could even initialise it with the first element of the array, but an empty string seems better.
Second go at this after writing some wrong code, this will return the longest string if you are happy with an empty string being returned for an empty array:
toDoItems.reduce("") { count($0) > count($1) ? $0 : $1 }
Or if you want nil, use
toDoItems.reduce(nil as String?) { count($0!) > count($1) ? $0 : $1 }
The problem is that the compiler cannot infer the types you are using for your seed and accumulator closure if you seed with nil, and you also need to get the optional type correct when using the optional string as $0.

How to get random element from a set in Swift?

As of Swift 1.2, Apple introduces Set collection type.
Say, I have a set like:
var set = Set<Int>(arrayLiteral: 1, 2, 3, 4, 5)
Now I want to get a random element out of it. Question is how? Set does not provide subscript(Int) like Array does. Instead it has subscript(SetIndex<T>). But firstly, SetIndex<T> does not have accessible initializers (hence, I can not just create an index with the offset I need), and secondly even if I can get the index for a first element in a set (var startIndex = set.startIndex) then the only way I can get to the N-th index is through consecutive calls to successor().
Therefore, I can see only 2 options at the moment, both ugly and expensive:
Convert the set into array (var array = [Int](set)) and use its subscript (which perfectly accepts Int); or
Get index of a first element in a set, traverse the chain of successor() methods to get to the N-th index, and then read corresponding element via set's subscript.
Do I miss some other way?
Starting with Swift 4.2, you can use randomElement:
let random = set.randomElement()
Probably the best approach is advance which walks successor for you:
func randomElementIndex<T>(s: Set<T>) -> T {
let n = Int(arc4random_uniform(UInt32(s.count)))
let i = advance(s.startIndex, n)
return s[i]
}
(EDIT: Heh; noticed you actually updated the question to include this answer before I added it to my answer... well, still a good idea and I learned something too. :D)
You can also walk the set rather than the indices (this was my first thought, but then I remembered advance).
func randomElement<T>(s: Set<T>) -> T {
let n = Int(arc4random_uniform(UInt32(s.count)))
for (i, e) in enumerate(s) {
if i == n { return e }
}
fatalError("The above loop must succeed")
}
In swift 3
extension Set {
public func randomObject() -> Element? {
let n = Int(arc4random_uniform(UInt32(self.count)))
let index = self.index(self.startIndex, offsetBy: n)
return self.count > 0 ? self[index] : nil
}
}
extension Set {
func randomElement() -> Element? {
return count == 0 ? nil : self[advance(self.startIndex, Int(arc4random()) % count)]
}
}
As per comments above re Swift updates, used a minor change for an extension to Set:
func randomElement() -> Element?
{
let randomInt = Int(arc4random_uniform(UInt32(self.count)))
let index = startIndex.advancedBy(randomInt)
return count == 0 ? nil: self[index]
}
If you want a 'random' element from a Set then you use:
/// A member of the set, or `nil` if the set is empty.
var first: T? { get }
Get the 0th index or the 1,000,000th index makes no difference - they are all an arbitrary object.
But, if you want repeated calls to return a likely different element each time, then first might not fit the bill.

Filter Array if Current Time is within TimeRange

My current function filters the array and returns an array of PFObjects with only "Type" = "Sushi". Now, I am trying to filter the array if current time is within a time range ("OpenHours" and "CloseHours")
The new Function passes dayOfWeek: Int, timeNow: String
"OpenHours" Example: [0, 6] = [sunday, monday]
["0011","0011","0011","0011","0011","0011","0011"]
"CloseHours" Example:
["2350","2350","2350","2350","2350","2350","2350"]
Current Function that filters "Type":
func filterRestaurants(filteredObject: String) {
//func filterOpenNow(dayOfWeek: Int, timeNow: String){
filteredRestaurantArray = unfilteredRestaurantArray.filter() {
if let type = ($0 as PFObject)["Type"] as? String { // Get value of PFObject
return type.rangeOfString("Sushi") != nil
} else {
println("nope")
return false
}
}
Basically I need to filter for objects where current time timeNow is between OpenHours and CloseHours for a given dayOfWeek
Edit:
What I've Tried so far:
I'm unsure how to get the position of a value in the PFObject array. Normal I would check if the timeNow is between OpenNow[dayOfWeek] and CloseNow[dayOfWeek]
Something like this (except with filter):
if OpenNow[dayOfWeek] ... CloseNow[dayOfWeek] ~= timeNow {
println("success")
}
I am not entirely sure what the properties of your PFObject instance are, so I'm going to make some assumptions and you will have to correct the keys to meet your needs.
Working with the function signature you provided is not entirely possible. This is because in order for it to work you would be accessing a list of PFObjects that were not provided to the function. While Swift does not keep you from doing that, it is generally not a wise design choice as you cannot be confident of the results of the function at any given point in time. So, to provide what is called referential transparency we will modify your function to also take in the collection of PFObjects.
With that collection we will then use the native filter function to determine which ones to return from the function. The filter function takes a collection and a closure. The closure takes one parameter, an element of the collection, and return a Bool. The closure is what determines if an element is kept or discarded.
Knowing that, we can build up the functionality you requested. I cannot guarantee there isn't a better way to do the first if let dance at the beginning, but it should get the job done.
func filterAreOpen(restaurants: [PFObject], forTime time: String, onDay day: Int) -> [PFObject] {
let openRests = filter(restaurants) { r in
if let openHours = r["OpenHours"] as AnyObject? as? [String] {
if let closeHours = r["CloseHours"] as AnyObject? as? [String] {
switch (openHours[day].toInt(), closeHours[day].toInt(), time.toInt()) {
case let (.Some(oh), .Some(ch), .Some(t)):
return oh...ch ~= t
default:
return false
}
}
}
return false
}
return openRests
}
And you would use it like this
let rests = [ // example objects that are replaced by your PFObject instances
[
"OpenHours":["0011","0012","0013"],
"CloseHours":["0023","0023","0023"],
"Name":"Restaurant1"
],
[
"OpenHours":["0014","0015","0016"],
"CloseHours":["0020","0020","0020"],
"Name":"Restaurant2"
]
]
let openRestaurants = filterAreOpen(rests, forTime: "0012", onDay: 1)
/* Results
[{
CloseHours = (
0023,
0023,
0023
);
Name = Restaurant1;
OpenHours = (
0011,
0012,
0013
);
}]
*/
Edit:
A quick explanation about the switch inside the closure. In Swift the switch statement is much more powerful than it was in the Objective-C days. It is capable of matching a value against a pattern, not just numbers.
So, in this case, the switch is matching a 3-tuple of Int?, (Int?, Int?, Int?). This is because String's toInt() method returns an Int?. switch is also able to bind matched patterns to local scoped constants. To do that we use the let keyword. To pattern match on an Optional value, you use the .Some(x) pattern. Since we have a 3-tuple we use .Some(x) three time, with the x replaced by some meaningful name. This way we have access to the three values we are interested in if the three toInt() calls evaluated to non-nil values.
If any of the String values could not evaluate to an Int, and so was nil, the default case is used, and returns false.
You can view the language book on conditional statements here.