Hi there I am new to Swift, I am trying to save Longitude and Latitude and place name from map's coordinate object to an Multidimensional array i.e:
Can anyone please help me how do i create these dynamically?
var pinArray[0][Lat] = 51.130231
var pinArray[0][Lon] = -0.189201
var pinArray[0][Place] = "Home"
var pinArray[1][Lat] = 52.130231
var pinArray[1][Lon] = -1.189201
var pinArray[1][Place] = "Office"
var pinArray[2][Lat] = 42.131331
var pinArray[2][Lon] = -1.119201
var pinArray[2][Place] = "Dinner"
You can make an array of dictionaries, but I suggest using structs instead.
Array of dictionaries
Create an empty array of dictionaries:
var pinArray = [[String:AnyObject]]()
Append dictionaries to the array:
pinArray.append(["lat":51.130231, "lon":-0.189201, "place":"home"])
pinArray.append(["lat":52.130231, "lon":-1.189201, "place":"office"])
But since your dictionaries hold two types of value (Double and String) it will be cumbersome to get the data back:
for pin in pinArray {
if let place = pin["place"] as? String {
print(place)
}
if let lat = pin["lat"] as? Double {
print(lat)
}
}
So, better use structs instead:
Array of structs
Create a struct that will hold our values:
struct Coordinates {
var lat:Double
var lon:Double
var place:String
}
Create an empty array of these objects:
var placesArray = [Coordinates]()
Append instances of the struct to the array:
placesArray.append(Coordinates(lat: 51.130231, lon: -0.189201, place: "home"))
placesArray.append(Coordinates(lat: 52.130231, lon: -1.189201, place: "office"))
It's then easy to get the values:
for pin in placesArray {
print(pin.place)
print(pin.lat)
}
Without more information, this is what I can offer.
var pinArray = [[AnyObject]]()
for location in mapLocations {
var innerArray = [location["latitude"], location["longitude"], location["place"]]
pinArray.append(innerArray)
}
Solution using an enum for Lat/Lon/Place (as you don't show us what these are):
enum Pos {
case Lat
case Lon
case Place
static let allPositions = [Lat, Lon, Place]
}
var myMatrix = [[Pos:Any]]()
myMatrix.append([.Lat: 51.130231, .Lon: -0.189201, .Place: "Home"])
myMatrix.append([.Lat: 52.130231, .Lon: -1.189201, .Place: "Office"])
myMatrix.append([.Lat: 42.131331, .Lon: -1.119201, .Place: "Dinner"])
/* check */
for (i,vector) in myMatrix.enumerate() {
for pos in Pos.allPositions {
print("myMatrix[\(i)][\(pos)] = \(vector[pos] ?? "")")
}
}
/*
myMatrix[0][Lat] = 51.130231
myMatrix[0][Lon] = -0.189201
myMatrix[0][Place] = Home
myMatrix[1][Lat] = 52.130231
myMatrix[1][Lon] = -1.189201
myMatrix[1][Place] = Office
myMatrix[2][Lat] = 42.131331
myMatrix[2][Lon] = -1.119201
myMatrix[2][Place] = Dinner */
Related
I have a tableView with different kinds of infos, each coming from a different array.
I could not work with dictionaries because then the list would have been unordered and I could not work with classes, because I have different lists with all kinds of dynamic entries (properties are always different etc.)
Here my problem:
I want to implement a search function. But when I use the filter function for one array, it changes of course based on the implemented condition but the other 5 stay the same => I can't reload the tableView because the array information does not match anymore ...
Here the arrays:
var categoryItemUIDs = [String]()
var categoryItemDescriptions = [String]()
var categoryItemLfdNrs = [Int]()
var categoryGivenOuts = [Bool]()
var categoryGivenTos = [String]()
var categoryGivenAts = [String]()
var categoryStorageLocations = [String]()
In the tableView(cellForRowAtIndexPath method):
cell.customTextLabel?.text = categoryItemLfdNrs[indexPath.row]
cell.customDetailTextLabel.text = categoryItemDescriptions[indexPath.row]
Here the searchBar(textDidChange) method:
self.categoryItemDescriptions.filter { $0.lowercased().contains(searchText.lowercased()) }
Now I get an array back with reduced size, but all the other arrays stay the same... Is there maybe another way to avoid this problem? I already tried type aliases but it did not work out.
I would appreciate any help!
Kind regards,
When it goes to such a big count of arrays, the time for your specific type comes.
The simple solution is to create something like
struct Category {
var uid: String
var description: String
// ...
var storageLocation: String
}
The you have simply something like
var items: [Category]
And you can still do simple things in cellForRowAtIndexPath
cell.customTextLabel?.text = items[indexPath.row].lfdnrs
cell.customDetailTextLabel.text = items[indexPath.row].description
And only 1 array to filter
items.filter { $0.description.lowercased().contains(searchText.lowercased()) }
So overall advice is to solve different problem (here I suggested the solution of the having your data in the app problem instead of filtering multiple arrays with one condition)
try
var categoryItemUIDs = ["aaa","bbb","ccc"]
var categoryItemDescriptions = ["ddd","eee","fff"]
var categoryItemLfdNrs = [0,1,2]
struct data {
var id = ""
var desc = ""
var item = 0
init(id :String, desc:String, item:Int)
{
self.id = id
self.desc = desc
self.item = item
}
}
//var cat = [data]()
//for i in 0..<categoryItemUIDs.count {
// cat.append(data(id:categoryItemUIDs[i], desc:categoryItemDescriptions[i],item:categoryItemLfdNrs[i] ))
//}
//more swift
let cat = (0..<categoryItemUIDs.count).map { (i) -> data in
return data(id:categoryItemUIDs[i], desc:categoryItemDescriptions[i],item:categoryItemLfdNrs[i] )
}
print (cat)
let catFilter = cat.filter { $0.id == "aaa" }
print (catFilter)
I declared a struct with 4 properties ( informationA, informationB, informationC, informationD ).
Also I've declared an array like this (The array includes some names of the my_struct properties as "strings" :
let keys = ["informationA", "informationB", "informationC"]
Now I'd like a for-loop to go through the "keys"-array and print out the struct property values for the current string ("informationA", "informationB", "informationC").
struct my_struct {
var informationA = "test"
var informationB = "test...test"
var informationC = "test...test"
var informationD = "test...test..."
}
func getInformation() {
let keys = ["informationA", "informationB", "informationC"]
for i in keys {
print(my_struct.i) // ERROR: Type 'my_struct' has no member 'i'
// should print ---> "test", "test...test", "test...test"
}
}
Using the code from above I'm getting this error ERROR: Type 'my_struct' has no member 'i'.
Is there a way to avoid this message and achieve the result I'd like to get?
What you are looking for is reflection:
struct MyStruct {
var informationA = "test"
var informationB = "test...test"
var informationC = "test...test"
var informationD = "test...test..."
}
func getInformation() {
let my_struct = MyStruct()
let keys = ["informationA", "informationB", "informationC"]
let m = Mirror(reflecting: my_struct)
let properties = Array(m.children)
for k in keys {
if let prop = properties.first(where: { $0.label == k }) {
print(prop.value)
}
}
}
I rewrite this code from php. And I find it difficult to make it work in swift.
var arrayOfData = [AnyObject]()
for index in 1...5 {
var dict = [String: AnyObject]()
dict["data"] = [1,2,3]
dict["count"] = 0
arrayOfData.append(dict)
}
for d in arrayOfData {
let data = d as AnyObject
// I want to update the "count" value
// data["count"] = 8
print(data);
break;
}
Presumably, you want to update the value inside of arrayOfData when you assign data["count"] = 8. If you switch to using NSMutableArray and NSMutableDictionary, then your code will work as you want. The reason this works is that these types are reference types (instead of value types like Swift arrays and dictionaries), so when you're working with them, you are referencing the values inside of them instead of making a copy.
var arrayOfData = NSMutableArray()
for index in 1...5 {
var dict = NSMutableDictionary()
dict["data"] = [1,2,3]
dict["count"] = 0
arrayOfData.addObject(dict)
}
for d in arrayOfData {
let data = d as! NSMutableDictionary
data["count"] = 8
print(data)
break
}
Assuming your array has to be of form '[AnyObject]' then something like this:
var arrayOfData = [AnyObject]()
for index in 1...5 {
var dict = [String: AnyObject]()
dict["data"] = [1,2,3]
dict["count"] = 0
arrayOfData.append(dict)
}
for d in arrayOfData {
// check d is a dictionary, else continue to the next
guard let data = d as? [String: AnyObject] else { continue }
data["count"] = 8
}
But preferably your array would be typed as an array of dictionaries:
var arrayOfData = [[String: AnyObject]]()
for index in 1...5 {
var dict = [String: AnyObject]()
dict["data"] = [1,2,3]
dict["count"] = 0
arrayOfData.append(dict)
}
for d in arrayOfData {
// swift knows that d is of type [String: AnyObject] already
d["count"] = 8
}
EDIT:
So the issue is that when you modify in the loop, you're creating a new version of the dictionary from the array and need to transfer it back. Try using a map:
arrayOfData = arrayOfData.map{ originalDict in
var newDict = originalDict
newDict["count"] = 8
return newDict
}
The most efficient way would be to find the index of the relevant values entry, and then replace that entry. The index is essentially just a pointer into the hash table, so it's better than looking up by key twice:
To update all the entries, you can loop through the indices one at a time:
for i in dictionary.values.indices {
dictionary.values[i].property = ...
}
To update a particular key, use:
let indexToUpdate = dictionary.values.index(forKey: "to_update")
dictionary.values[i].property = ...
For example, I have an array like var myArray = ['player_static.png', 'player_run0.png', 'player_run1.png', 'player_run2.png', 'player_jump0.png', 'player_jump1.png']
Is there any simple way to get only the "player_runX.png" images?
You can use filter to get all elements that hasPrefix("player_run"):
let myArray = ["player_static.png", "player_run0.png", "player_run1.png", "player_run2.png", "player_jump0.png", "player_jump1.png"]
let playerRuns = myArray.filter{$0.hasPrefix("player_run")}
print(playerRuns) //["player_run0.png", "player_run1.png", "player_run2.png"]
One way to do this would be to iterate over the array and retrieve the elements that match the pattern. A very quick sample would be something like this:
var myArray = ["player_static.png", "player_run0.png", "player_run1.png", "player_run2.png", "player_jump0.png", "player_jump1.png"]
func getSubArray(array:[String],prefix:String) -> [String]
{
var newArray = [String]()
for img in array
{
if img.substringToIndex(img.startIndex.advancedBy(prefix.characters.count)) == prefix
{
newArray.append(img)
}
}
return newArray
}
var test = getSubArray(myArray, prefix: "player_run")
I have a table view which displays a user's Name, Company Name and Photo (PFFile). Each tableView row I have has all of this information in it.
I am using UISearchBarDelegate and IB to implement a search function to filter by the user's Name. It is finding the correct user but I have not been able to also update the company photo.
How do I filter the other arrays? The items I need from the arrays will be at the same index as the ones taken from the user's Name array.
EDIT: I am trying a different data structure and am receiving array index out of range, updated code below:
var filterArray = [User]() //<-- globally declared
var userArray = [User]() //< Global
class User {
var name: String?
var company: String?
init (name: String?, company: String?) {
self.name = name
self.company = company
}
}
//In a class which populates the search arrays
for object in unwrappedSucceeded {
let username = object.valueForKey("username") as! String
let companyName = object.valueForKey("companyName") as! String
let user = User(name: username, company: companyName)
userArray.append(user)
}
//tableViewController
func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {
filterArray.removeAll(keepCapacity: false)
if searchText.characters.count != 0 {
isSearch = true
self.search(searchText)
} else {
isSearch = false
}
}
func search(text: String) -> Void {
filterArray = userArray.filter({$0.name == text})
}
//In cellForRowAtIndexPath
cell.usernameCell.text = filterArray[indexPath.row].name //ARRAY INDEX OUT OF RANGE
Like I said you strongly recommend to group each user's info into one big container, therefore we could use array of struct or class, then it comes easier to filter.
schematic for the container:
struct Container
{
var username:String?
var companyName:String?
var photo:UIImage?
}
your main array will be : var arrayofData = [Container]()
Now when you are query your objects from parse, inside of your query function
// after you called the findObjectsWithBackgroundBlock()
// let's assume you check for error and if the [PFObject] is empty or not
for one in objectsFromParse
{
let photoToget = one["Photo"] as! PFFile
// next step should be to get the image data right :)
{
// let's assume that is the block when get the image data right:)
// check your data and assign it to some UIImage
// then
let userRepresentation = Container() //<-- we are creating a single object representation for each user
let username = one["username"] as! String //<--data we got from Parse
let companyName = one["companyName"] as! String
let userImage = //the UIImage which contains the data
userRepresentation.username = username
userRepresentation.companyName = companyName
userRepresentation.photo = userImage
// then we append
arrayOfData.append(userRepresentation)
}
}
Now we have all data into our array, so let's filter by username and also I hope you configure your tableView so when you have data from filter or regular array.
var filterArray = [Container]() //<-- globally declared
func search(text: String) -> Void
{
filterArray = arrayOfData.filter(){ (Container) -> Bool in
let range = Container.name!.rangeOfString(text, options:NSStringCompareOptions.CaseInsensitiveSearch) return range != nil }
// then you are good to go
}
let arr1 = [10,20,40]
let e1 = arr1.enumerate()
let arr2 = ["a","b","c"]
let f1 = e1.filter { $0.element % 20 == 0 }
let f2 = arr2.enumerate().filter { j, _ in
f1.contains { i, _ in
i == j
}
}
print(f1.map{$0.element}, f2.map{$0.element})
// [20, 40] ["b", "c"]
now you have both arrays "filtered". the best, what you can do is redesigning your data model!