Sort an object array [Venue] by its property using a sorted array [Geofire Keys] as reference? - arrays

I have built the following "helper" function, which takes as parameters:
'unsortedArray': The array of Venue objects required to be sorted by its .venueID string property
'sortingGeoArray': the Geofire string keys to be used as reference to order the unsorted array above.
and it returns a sorted array of type [Venue] via an escaping handler.
I have tried to implement this nice and simple solution suggested on the following thread:
'Sorting a Swift array by ordering from another array'
func sortVenuesArraybyGeofireKeys(unsortedArray: [Venue], sortingGeoArray: [String] , handler: #escaping (_ sortedArray: [Venue]) -> ()){
let ordering = Dictionary(uniqueKeysWithValues: sortingGeoArray.enumerated().map { ($1, $0) })
let sorted: [Venue] = unsortedArray.sorted{ ordering[$0.venueID]! < ordering[$1.venueID]! }
handler(sorted)
}
I have tried the sorting code above in multiple places through out my code by I always get a "Unexpectedly found nil while unwrapping an Optional value" at the following line (works when i test it an playground):
let sorted: [Venue] = unsortedArray.sorted{ ordering[$0.venueID]! < ordering[$1.venueID]!
On my debug window below, I have a feeling that the .map function into the let 'ordering' is not working and therefore finding nil on the next line
any help would be appreciated.
UPDATE: thanks to the support below, it appears that my Geofire query below in particular the 'append' to venueGeoKeys [string] array is not appending the key values, hence why found nil when I execute the function to sort.
let query = self.GEOFIRE_VENUES.query(at: location, withRadius: 1000)
query.observe(.keyEntered) { (key: String!, location: CLLocation!) in
self.venueGeoKeys.append(key)
}

Related

Create a shuffled Array that I can use in all functions thereafter

Hi I'm trying to shuffle an array which I can then use in all my IBAction functions thereafter in the order that I shuffled them. Example of code which is the same idea as what I have...
Class ViewContoller: UIViewContoller
Let numbers = [1,2,3,4,5]
override func viewDidLoad() {
super.viewDidLoad() }
I've tried to write a function here to create an array which has been shuffled so I can use that shuffled array ...
func shuffledArray() -> Array<Int> {
let numbersArray = numbers.shuffled()
return numbersArray }
let myListOfShuffledNumbers = shuffledArray()
Now this works in playground.. but when I try this my viewcontroller.swift I get error 'cannot use instance member 'shuffledArray' within property initializer; property initializers run before self is available.
So I know if I'm in a IBAction func I can run the function let shuffledNumbers = shuffledArray()and get an Array of shuffled numbers to use but my problem is I need to use that same shuffle order in another IBAction function. So how can I make a universal array of shuffled numbers to use anywhere on my view controller.
I've tried lazy var myListOfShuffledNumbers = shuffledArray()
This gets rid of my error but it doesn't help me because I get a 2 different list order when I use 'myListOfShuffledNumbers' in 2 different IBAction functions
What can I do ?? Thanks
You can define a property that you can access from anywhere in your ViewContoller, like this:
class ViewContoller: UIViewContoller {
let unshuffledNumbers = [1,2,3,4,5]
var myListOfShuffledNumbers = [Int]() /// here! it's still empty at first
#IBAction func firstButtonPressed(_ sender: Any) {
myListOfShuffledNumbers = unshuffledNumbers.shuffled()
}
#IBAction func secondButtonPressed(_ sender: Any) {
/// do something with myListOfShuffledNumbers here...
}
}
At first, myListOfShuffledNumbers is still an empty array of Ints. But inside your first IBAction, you can assign that to unshuffledNumbers.shuffled(). Then, you can access it from inside your second IBAction.
Also it might be easier to just write [Int] instead of Array<Int> -- they are the same thing.
func shuffledArray() -> [Int] {
let numbersArray = numbers.shuffled()
return numbersArray
}
If you want an array that is shuffled then just convert it to a set. Sets will not keep the original order of the array.

Swift picker array error

var pickerData: [[String]] = [String]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
// Connect data:
self.gothramPicker.delegate = self
self.gothramPicker.dataSource = self
pickerData = [
[["A"],["i","ii","iii"]],
[["B"],["iv","v","vi","vii"]]
]"
getting error of Contextual type 'String' cannot be used with array literal. 
my picker has two sections and each item if selected in the first picker has its own subdivisions to select from the second picker.
I want to use Array in Array and each main array has subarrays of different list. When I select first array in the picker it should show only the subitems of the particular Array item. For which when I add the double Brackets, I am getting the error. Also please let me know any error in the code
#sureshtrb , I am not posting answer because you don't asked question here. Frankly, you are troubling in Syntax understanding only.
You were declaring picketData of type Array([]) that contains array of string[[String]], and by putting ["A"] in place of string, you are adding an array ["A"] in place of String "A".
As per your real problem, I suggest you to go with 2 diff array or use dictionary.
The below code compiles.
var pickerData: [[String]] = [[String]]()
override func viewDidLoad() {
super.viewDidLoad()
pickerData = [
["A","i","ii","iii"],
["B","iv","v","vi","vii"]
]
}

How to check a value is inside nested dictionary swift

I have a problem concerning my nested dictionary.
var level1Dictionary = [String : [String : String]]()
var ChosenDeckLabel = [String]
textview.text
I want to see if the dictionary contains a certain value within my, else if statement as such:
else if level1Dictionary[ChosenDeckLabel[textview.text]] != nil {
this returns error:
Cannot subscript value of type String with an index of type String!
How should I cast it to check if the nested dictionary contains the value?
Dictionaries are optionals by default because they are not sure if a key/value pair exist. Be sure to include your "!" AND "?" to wrap and unwrap your data being passed.
Arrays offer subscripting via integers and ranges as seen in Swift's API:
public subscript (index: Int) -> Element
public subscript (subRange: Range<Int>) -> ArraySlice<Element>
You're trying to subscript via a string which is throwing the error. You need to get the index of the element in the array and then use that to subscript the array to get the value: e.g.
let dictOfDicts = [String : [String : String]]()
var arrayOfStrings: [String] = ["a", "b", "c"]
let stringToCheck = "a"
dictOfDicts["a"] = ["some": "thing"]
if let index = array.indexOf(stringToCheck) {
if dictOfDicts[array[Int(index)]] != nil {
// do something
}
}
I think this is what you intend to do:
else if level1Dictionary[strIndex1][strIndex2] != nil {
When you are doing this:
level1Dictionary[ChosenDeckLabel[textview.text]]
you are trying to access the ChosenDeckLabel array using a String subscript:
ChosenDeckLabel[textview.text]
which is not a valid operation. Arrays are Int indexed and not string indexed.

PFObject Array Sort

I'm using Parse and I have an array of PFObjects called "scorecardData". Each PFObject has a "score" property that is of type Int. I'm trying to sort my array by "score" but I'm getting the following error: "Binary operator '<' cannot be applied to two 'AnyObject?' operands". I'm not sure what I'm doing wrong here. I also tried down casting the objectForKey("score") as! Int but its not letting me do this. Any suggestions? Thanks in advance.
var scorecardData = [PFObject]()
scorecardData.sortInPlace({$0.objectForKey("score") < $1.objectForKey("score")})
You declared scorecardData variable as Array of PFObject. Why are you trying access PFObject property using objectForKey: reserved? Anyway I am not parse expert. But if you declared your array as [PFObject] you can use:
scorecardData.sortInPlace({$0.score < $1.score})
But this won't work unless you subclass PFObject for a more native object-oriented class structure. If you do that remember also to specify:
var scorecardData = [YOUR_NEW_CLASS]()
I strongly recommend subclassing PFObject to make use of all swift type-safe goodies.
But if you want to keep your data structure you can use:
scorecardData.sortInPlace({($0["score"] as! Int) < ($1["score"] as! Int)})
Keep in mind that it's dangerous, and in future avoid it.
If you want to Sort your array of PFOject... You can do this
extension Array where Element:PFObject {
func sort() -> [PFObject] {
return sort { (first, second) -> Bool in
let firstDate = first.objectForKey("time") as! NSDate//objectForKey(Constants.Parse.Fields.User.fullName) as? String
let secondDate = second.objectForKey("time") as! NSDate//objectForKey(Constants.Parse.Fields.User.fullName) as? String
return firstDate.compare(secondDate) == .OrderedAscending
}
}
}
Have you tried doing this?
var query = PFQuery(className:"ScoreCard")
// Sorts the results in ascending order by the score field
query.orderByDescending("score")
query.findObjectsInBackgroundWithBlock {

Append unique values to an array in swift

I haven't found anything on that in Swift. Have found how to find unique values on an array, but not this. Sorry if it sounds quite basic...
But I have the following array
var selectedValues = [String]()
And the following value that comes from a Parse query
var objectToAppend = object.objectForKey("description")! as! String
this is how I'am doing it at the moment.
self.selectedHobbies.append(objectToAppend)
But because the query happens repeated times, it ends up appending repeated values. It works, but I just want to not waste memory and only keep unique values.
Any ideas on how to solve that in swift?
You can use a Set which guarantees unique values.
var selectedValues = Set<String>()
// ...
selectedValues.insert(newString) // will do nothing if value exists
Of course, the elements of a Set are not ordered.
If you want to keep the order, just continue with the Array but check before you insert.
if !selectedValues.contains("Bar") { selectedValues.append("Bar") }
I guess that your problem was resolved but I add my answer for next developers who's facing same problem :)
My solution is to write an extension of Array to add elements from an array with a distinct way:
here the code :
extension Array{
public mutating func appendDistinct<S>(contentsOf newElements: S, where condition:#escaping (Element, Element) -> Bool) where S : Sequence, Element == S.Element {
newElements.forEach { (item) in
if !(self.contains(where: { (selfItem) -> Bool in
return !condition(selfItem, item)
})) {
self.append(item)
}
}
}
}
example:
var accounts: [Account]
let arrayToAppend: [Account]
accounts.appendDistinct(contentsOf: arrayToAppend, where: { (account1, account2) -> Bool in
return account1.number != account2.number
})

Resources