Delcare and access multidimensional array in swift - swift

I would like to declare an array which should look like this:
myArray
->Car (Array)
- Ford
- Audi
- ...
->Color (Array)
- Red
- Green
- ...
->Wheels (Simple String, eg "4")
->Roof (Simple String, eg, "NO")
Im stuck in space to delare and access this type of array. Thank you for any help!

I think creating custom objects (with classes or structs) would be better suited for you, like this for example:
class Car {
var brand: String?
var color: String?
var wheels: Int?
init(brand: String, color: String, wheels: Int = 4) {
self.brand = brand
self.color = color
self.wheels = wheels
}
}
let myAudi = Car(brand: "Audi", color: "red")
let myFord = Car(brand: "Ford", color: "green", wheels: 3)
var myCars = [Car]()
myCars.append(myAudi)
myCars.append(myFord)
for aCar in myCars {
println("My \(aCar.brand!) is \(aCar.color!) and has \(aCar.wheels!) wheels.")
}
Result:
My Audi is red and has 4 wheels.
My Ford is green and has 3 wheels.
Note: have a look at this, it will help.

you can create an array with each elements as an array. For example
var tempArr : [[String]] = [["Ford","Audi"],["Red","Green"],["4"],["NO"]]
based on your question i guess you need a dictionary and not an array
var tempDict : [String : [String]] = ["Car" : ["Ford","Audi"],.....]

Related

How to Sort the first dimension of a 2D Array Swift

Im trying to sort the columns of a CSV file,the contents of the CSV is provided in string
Beth,Charles,Danielle,Adam,Eric\n
17945,10091,10088,3907,10132\n
2,12,13,48,11
Converted String to 2D Array
[["Beth", "Charles", "Danielle", "Adam", "Eric"], ["17945", "10091", "10088", "3907",
"10132"], ["2", "12", "13", "48", "11"]]
How can i sort the only the first dimension of the 2D array or the Names in the 2D Array and still keep the mappings of the other dimension, i don't know how to explain this properly, but i hope the details below will help you understand what i want to achieve.
Adam,Beth,Charles,Danielle,Eric\n
3907,17945,10091,10088,10132\n
48,2,12,13,11
I want to achieve this with the names sorted and the other values in the other arrays mapping to the names like below,
[["Adam", "Beth", "Charles", "Danielle", "Eric"], ["3907", "17945", "10091", "10088",
"10132"], ["48", "2", "12", "13", "11"]]
Using this approach is not working but sorts the whole array
let sortedArray = 2dArray.sorted(by: {($0[0] as! String) < ($1[0] as! String) })
[["3907", "17945", "10091", "10088", "10132"], ["48", "2", "12", "13", "11"], ["Adam", "Beth", "Charles", "Danielle", "Eric"]]
Below if the full code
var stringCSV =
"Beth,Charles,Danielle,Adam,Eric\n17945
,10091,10088,3907,10132\n2,12,13,48,11";
var csvFormatted = [[String]]()
stringCSV.enumerateLines { line , _ in
var res = line.split(separator: ",",omittingEmptySubsequences:
false).map{ String($0) }
for i in 0 ..< res.count {
res[i] = res[i]
}
csvFormatted.append(res)
}
print(csvFormatted)
let sortedArray = csvFormatted.sorted(by: {($0[0] as! String)
< ($1[0] as! String) })
print(sortedArray)
Using "associated" arrays always ends up being messy.
I would start by creating a struct to represent each object (You haven't said what the numbers are, so I have picked a couple of property names. I have also kept String as their type, but converting to Int is possibly better, depending on what the data actually represents).
struct Person {
let name: String
let id: String
let age: String
}
Now you can combine the arrays and use that to build an array of these structs. Then you can sort by the name property.
let properties = zip(sourceArray[1],sourceArray[2])
let namesAndProperties = zip(sourceArray[0],properties)
let structArray = namesAndProperties.map { (name,properties) in
return Person(name: name, id: properties.0, age: properties.1)
}
let sortedArray = structArray.sorted {
return $0.name < $1.name
}

Loop through multiple lists

I have three dictionaries structured as [Int : Int]. Each dictionary is associated with an object. The key of the dictionary is a value of the object, and the value of the dictionary is how many object of that key should exist. One object holds all of these objects. So it would look at little like this.
class Cart {
var fruit: Fruit?
var cereal: Cereal?
var juice: Juice?
}
class Food {
var key: Int?
var text: String?
}
class Fruit: Food ...
class Cereal: Food ...
class Juice: Food ...
The idea is that the user has entered values for each possible option in text boxes and then a dictionary for each object is made. I need to make carts for every object, and each cart needs to holdas many objects as it can. If I have 6 fruits, 5 cereals, and 6 juices, then there should be 6 carts, one of them missing a cereal.
I have everything working up to figuring out how to actually put them together. Here's my loop.
for (key, value) in fruitValues {
for _ in 0..<value {
print(realm.objects(Fruit.self).filter("value == \(key)"))
}
}
How can I best loop through one array and get values from the others?
Here is my attempt to solve this, not the most compact solution but I couldn't figure out anything shorter. Assume our 3 dictionaries from the users selection
let dictFruit = [1: 2, 2: 4]
let dictCereal = [3: 5]
let dictJuice = [4: 3, 5:2, 6: 1]
for this example I use a dictionary as my Food types storage
let products = [1: Fruit(key: 1), 2: Fruit(key: 2), 3: Cereal(key: 3), 4:Juice(key: 4), 5:Juice(key: 5), 6:Juice(key: 6)]
First step is to flatten out the dictionaries to arrays of Food objects which is done with this function
func expandSelected<T: Food>(from dict: [Int: Int]) -> [T] {
return dict.reduce(into: []) {
if let food = products[$1.key] as? T {
$0.append(contentsOf: Array(repeating: food, count: $1.value))
}
}
}
Which I then used
let fruits: [Fruit] = expandSelected(from: dictFruit)
let juices: [Juice] = expandSelected(from: dictJuice)
let cereals: [Cereal] = expandSelected(from: dictCereal)
then for the last part I used an old fashioned for loop to create the Cart items
var carts = [Cart]()
for i in 0..<max(fruits.count, juices.count, cereals.count) {
let cart = Cart()
cart.cereal = cereals.count > i ? cereals[i] : nil
cart.fruit = fruits.count > i ? fruits[i] : nil
cart.juice = juices.count > i ? juices[i] : nil
carts.append(cart)
}

Delete array of structs from an array of structs Swift

I have an array of structs that looks like this:
struct minStruct {
var namn:String?
var imag:UIImage?
var rea:String?
var comp:String?
}
var structArr = [minStruct]()
If I would like to remove a specific struct from that array, I could simply do this:
var oneStruct = minStruct(namn: "Name", imag: UIImage(named: "Image"), rea: "Something", comp: "Something")
if structArr.filter({$0.namn == oneStruct!.namn}).count > 0 {
structArr = structArr.filter({$0.namn != oneStruct!.namn})
}
However, what I would like to do is to remove an array of structs from structArr. Something like this:
structArr = [minStruct(namn: "Name", imag: UIImage(named: "Image"), rea: "Something", comp: "Something"), minStruct(namn: "secondName", imag: UIImage(named: "secondImage"), rea: "Something2", comp: "Something2"), minStruct(namn: "thirdName", imag: UIImage(named: "thirdImage"), rea: "Something3", comp: "Something3")]
var arrToDelete = [minStruct(namn: "Name", imag: UIImage(named: "Image"), rea: "Something", comp: "Something"), minStruct(namn: "secondName", imag: UIImage(named: "secondImage"), rea: "Something2", comp: "Something2")]
So what I want to do is to delete all the items that are inside of arrToDelete from arrStruct. In this example, arrToDelete contains two of the three structs that structArr contains. I want to delete these two structs and keep the one struct that arrToDelete did not contain. I hope that I was clear enough!
Hashable
So we have struct. First of all let's make it Hashable
struct Element: Hashable {
var name: String?
var image: UIImage?
var rea: String?
var comp: String?
var hashValue: Int { return name?.hashValue ?? image?.hashValue ?? rea.hashValue ?? comp.hashValue ?? 0 }
}
func ==(left:Element, right:Element) -> Bool {
return left.name == right.name && left.image == right.image && left.rea == right.rea && left.comp == right.comp
}
Data
Next we have these 2 arrays
let elms : [Element] = [
Element(name:"a", image:nil, rea:nil, comp:nil),
Element(name:"b", image:nil, rea:nil, comp:nil),
Element(name:"c", image:nil, rea:nil, comp:nil)
]
let shouldBeRemoved: [Element] = [
Element(name:"b", image:nil, rea:nil, comp:nil),
Element(name:"c", image:nil, rea:nil, comp:nil)
]
Solution #1
If you DO NOT care about the original sorting you can use
let filtered = Array(Set(elms).subtract(shouldBeRemoved))
Solution #2
If you DO care about the original sorting
let shouldBeRemovedSet = Set(shouldBeRemoved)
let filtered = elms.filter { !shouldBeRemovedSet.contains($0) }
Why didn't I just write this?
let filtered = elms.filter { !shouldBeRemoved.contains($0) }
The line above is a correct solution. However invoking contains on
Array is generally slower (usually n/2 checks need to be performed)
than invoking it on a Set (usually a single check).
Structs for some reason aren't really allowed to be compared using Boolean logic, thus it's hard to even search an array of Structs for an item or the item's index. So for instance, if you wanted to search an array of Structs for a specific struct and then delete it:
First, you'd need to go to your struct file and give it a protocol which allows the struct to be used in boolean logic, so...
struct Persons: Equatable {
struct properties...
struct init....
}
Then you could search within the array using array methods such as...
firstIndex(of: theStructYourLookingFor)
lastIndex(of: _)
or any other boolean-related array methods.
This could be newish as of Swift 4.0 and greater-- April 2019

SWIFT append data to dictionary

I'm having trouble coding an apparently simple task. I want to add new client profile data to a client profile dictionary (clientDatabase) but keep getting errors - can't seem to append - error: value of type '(String: clientProfile)' has no member 'append' (see error at bottom of code)
Any insights you can provide are greatly appreciated.
Thanks,
Bill
//: Playground - noun: a place where people can play
import UIKit
import Foundation
/*
code copied from B-C Dev Database - structs but simplified with fewer variables
goal is to getappend new client to work.
*/
/*
Globals: these go in main.swift file
*/
struct clientProfile {
var firstName: String = ""
var lastName: String = ""
var group: Int = 0
}
//var clientDatabase : NSMutableDictionary! = [String:clientProfile]()
var clientDatabase:[String:clientProfile]
/* sample data template: phone is key, sub array is;
(firstName: "", lastName: "",pilatesGroup: )
*/
clientDatabase = [
"1234567": clientProfile(firstName: "Sally", lastName: "Sillious", group: 3),
"2345678": clientProfile(firstName: "Sue", lastName: "Parker",group: 8),
"3456789": clientProfile(firstName: "Bob", lastName: "Parker", group: 2),
"5678901": clientProfile(firstName: "Jim", lastName: "Beam", group: 12)
]
clientDatabase.count
// so far so good
/*
add new client
declare local variables in scene swift files where used
*/
var firstName: String = ""
var phone:String = ""
var newPhone: String = ""
var newFirstName: String = ""
var newLastName: String = ""
var newGroup: Int = 0
// define struct using these input variables for values but with same keys as (global) clientDatabase
struct newClientProfile {
var firstName: String = newFirstName
var lastName: String = newLastName
var group: Int = newGroup
}
// put newClientProfile into newClientDictionary
var newClientDatabase:Dictionary = [String:newClientProfile]()
// input values from scene - UITextFields
newPhone = "4567890"
newFirstName = "Super"
newLastName = "Dave"
newGroup = 14
// test that all values are where they should be
clientDatabase
clientDatabase.count
newClientDatabase = [newPhone:newClientProfile()]
newClientDatabase.count
// ok so far
//the following line returns an error
clientDatabase.append(newClientDatabase)
// can't seem to append - error value of type '(String: clientProfile)' has no member 'append'
Two things. First of all clientDatabase is a dictionary which doesn't have append, instead you'll have to iterate through the other dictionary and insert its elements into clientDatabase.
The other issue is that clientDatabase and newClientDatabase aren't the same type. The first one is [String : clientProfile] and the second is [String : newClientProfile]. You'll have to convert the values from one type to the other to combine the dictionaries.
Looking deeper into the code there some misunderstandings about the language. For example:
struct newClientProfile {
var firstName: String = newFirstName
var lastName: String = newLastName
var group: Int = newGroup
}
// put newClientProfile into newClientDictionary
var newClientDatabase:Dictionary = [String:newClientProfile]()
You're creating a struct just for the purpose of containing a single set of values when you already have clientProfile. Instead you could do:
var newClientProfile = clientProfile(firstName: newFirstName, lastName: newLastName, group: newGroup)
This will create a variable which is an instance of clientProfile and stores the information you want. However, you have the other variables defined as empty values.
Here's a cleaned up version of your code, take a look at it and let me know if you have any questions.
struct ClientProfile { // Convention is to use initial caps for enum, struct, class
let firstName: String
let lastName: String
let group: Int
}
var clientDatabase = [
"1234567": ClientProfile(firstName: "Sally", lastName: "Sillious", group: 3),
"2345678": ClientProfile(firstName: "Sue", lastName: "Parker",group: 8),
"3456789": ClientProfile(firstName: "Bob", lastName: "Parker", group: 2),
"5678901": ClientProfile(firstName: "Jim", lastName: "Beam", group: 12)
]
// input values from scene - UITextFields
let newPhone = "4567890"
let newFirstName = "Super"
let newLastName = "Dave"
let newGroup = 14
// define struct using these input variables for values but with same keys as (global) clientDatabase
let newClientProfile = ClientProfile(firstName: newFirstName, lastName: newLastName, group: newGroup)
let newClientDatabase = [newPhone:newClientProfile]
for (phone,client) in newClientDatabase {
clientDatabase[phone] = client
}

Extract value from struct that meet condition

I have a struct for different animals, and values for these animals. Im adding animals to it.
struct Animal {
var type: String
var weight: String
var cost: String
}
var animals = [Animal]()
func addAnimal(type: String, weight: String, cost: String){
animals.append(Animal(type: type, weight: weight, cost: cost))
}
addAnimal("monkey", "80", "300")
addAnimal("zebra", "200", "500")
addAnimal("monkey", "50", "250")
I want to say, if type == "monkey" then return all weights for monkeys. In this example I would want the code to return values "80" and "50".
I'm new to coding so any advice on this would be helpful. Thank you
You can combine filter and map to accomplish what you want as follow:
let monkeysWeights = animals.filter{$0.type == "monkey"}.map{$0.weight}
println(monkeysWeights) // ["80", "50"]