How to pull data out of dictionaries - ios

I'm trying to figure out how to basically create an if statement that is able to find out how many goals are scored in the first half of a soccer match that Barcelona played in. The stats are all there with the number beside then representing the minute the goal was scored in. So how would I write my if statement like that. I know how to do it with a simple variable with just a single value like either a string or a number but how would I find a value in a dictionary with a specific number value and the specific number value I want?
var barcelonavsRealMadrid1 = [[String : Int]]() // if you want this globally
var barcelonavsRealMadrid1goals : [String : Int] = ["barcelonaGoal":21,"RealMadridGoal":23]
var barcelonavsRealMadrid1penaltys : [String : Int] = ["RealMadridPenalty":21,"barcelonaPenalty":23]
if barcelonavsRealMadrid1goals == ["barcelonaGoal":21] {
print("Fond it")
}

You can retrieve data in dictionaries easily:
barcelonavsRealMadrid1goals["barcelonaGoal"]
But I have to tell you that I think your data structure will not work the way you want it to. For example, you can't put several goals for one team into the dictionary.
Build classes for your data and encapsulate properly. Don't throw "everything in something" and build on some magic element names. Understand the concepts you are using.
I think reading a good programming introduction might be called for, and then the Swift programming guide comes into mind.
Edit
Here's a very simplified example of what a game data structure could look like:
class Game {
var team1 : String
var team2 : String
var minutesOfGoalsTeam1: [Int]
var minutesOfGoalsTeam2: [Int]
init(team1 t1 : String, team2 t2: String) {
team1 = t1
team2 = t2
minutesOfGoalsTeam1 = []
minutesOfGoalsTeam2 = []
}
func goalsTeam1() -> Int {
return minutesOfGoalsTeam1.count
}
func goalsTeam2() -> Int {
return minutesOfGoalsTeam2.count
}
func addGoal(team : String, minute : Int) {
if team == team1 {
minutesOfGoalsTeam1.append(minute)
}
if team == team2 {
minutesOfGoalsTeam2.append(minute)
}
}
func winner() -> String {
if goalsTeam1() > goalsTeam2() {
return team1
}
if goalsTeam2() > goalsTeam1() {
return team2
}
return "DRAW"
}
}
let game = Game(team1: "Barcelona", team2: "Madrid")
game.addGoal("Barcelona", minute: 5)
game.winner()
Adding penalties is left as an exercise. This is really just the tip of the ice berg. There are hundreds of ways to do it, and I wouldn't consider this the best approach. For example, one could think about a dedicated team class. Or using some game event class with subclasses for goals and penalties, and adding those to an array and looping over it for the concrete data.

var goalCount=0
for (goal,numbers) in barcelonavsRealMadrid1{
for(var number in numbers){
if(number < 45)
goalCount++
}
}
i think it will work

Related

How can I create an algorithm that appends a name onto an array?

I've been going through Glassdoors for companies I am interested in interviewing with and saw one that said the company asked them a question about "appending a name onto a list". I have been trying to figure out how to go about that. I know the function will take an array and will return an array as well. Can someone help me write that out and explain it to me? Attached is my version, which does not work. It just prints out the existing string twice
func addNameToList(_ names: [String]) -> [String] {
var names = ["Ben", "Sam", "Ken"]
var results = [names[0]]
for name in names {
names.append(name)
print(names)
}
return results
}
addNameToList([String("Louis")])
if you are need to add one or more string value with Existing string array you can use like below
var globalArrayString = ["One","two","three"]
func appendString(_ currentString: String...) -> [String] {
globalArrayString.append(contentsOf: currentString)
return globalArrayString
}
// if you need to add more String,Call like below
let combinedArray = appendString("four","five")
print(combinedArray)

Not correctly referencing random number in Swift object

I have an array of objects where each object has an Exercise Name and a random number of reps.
I then have a function to generate a random workout (with between 3 - 6 exercises in it)
However, when I print it the reps are almost always 1, 2 or occasionally 14, despite loading it 30 times or so.
Am I doing something wrong here?
Here's my objects and struct :
struct exerciseInWorkout {
let name : String
let reps : Int
}
let exerciseBankArray = [
exerciseInWorkout(name: "Squat", reps: (Int(arc4random_uniform(10)))),
exerciseInWorkout(name: "Push Ups", reps: (Int(arc4random_uniform(5)))),
exerciseInWorkout(name: "Viking Press", reps: (Int(arc4random_uniform(20)))),
]
and here's my function :
func generateWorkout(){
let possibleExercises = exerciseBankArray
let numberOfExercisesKey = Int(arc4random_uniform(4) + 3)
let workoutSet : [exerciseInWorkout] = (1...numberOfExercisesKey).map { _ in
let randomKey = Int(arc4random_uniform(UInt32(possibleExercises.count)))
return exerciseInWorkout(name: exerciseBankArray[randomKey].name, reps: exerciseBankArray[randomKey].reps)}
print (workoutSet)
}
}
Also, is there a way to create a set of these to avoid the same exercise coming up twice? I tried using Set but didn't seem to work at all.
Lastly, when I print it, each object is prepended with "project.exerciseInWorkout... is there any way to just print/return the clean array i.e. [["name: "press ups", reps: 12], [name:xyz, reps: 30]]?
Reason being I want to pass this to a new VC to put in a table view next and presume I need a clean array to do that.
Short Answer
It looks like your array exerciseBankArray is stored globally, which means exerciseInWorkout components are initialized once for the whole app. Having said that, it is then normal that the number of reps is always the same, the arc4random_uniform is only executed on the first access.
Quick Fix
If you want to keep the same structure, I recommend this
remove the arc4random_uniform from exerciseBankArray and just write the maximum amount of reps you want
let exerciseBankArray = [
exerciseInWorkout(name: "Squat", maxReps: 10),
exerciseInWorkout(name: "Push Ups", maxReps: 5),
exerciseInWorkout(name: "Viking Press", maxReps: 20)
]
Call the random function inside generateWorkout() like this
func generateWorkout(){
let possibleExercises = exerciseBankArray
let numberOfExercisesKey = Int(arc4random_uniform(4) + 3)
let workoutSet : [exerciseInWorkout] = (1...numberOfExercisesKey).map { _ in
let randomKey = Int(arc4random_uniform(UInt32(possibleExercises.count)))
return exerciseInWorkout(
name: exerciseBankArray[randomKey].name,
reps: Int(arc4random_uniform(exerciseBankArray[randomKey].maxReps))
)
}
}
Longer Improvements
If you're open to making a better architecture for your code, here are some suggestions
Remove global variables
Split your model for exercise into two classes / structs:
One that represents an actual exercise
struct WorkoutExercise {
let name: String
let reps: Int
}
One that represents a generator of exercise
struct WorkoutExerciseGenerator {
let name: String
let maxReps: Int
// Add any other parameter you need to generate a good exercise
func generate() -> WorkoutExercise {
return WorkoutExercise(
name: name,
reps: Int(arc4random_uniform(maxReps))
)
}
}
More Improvements / Q&A
When you say remove global variables do you mean store the array of exercises in each VC that needs them? I just thought that would be “repeating myself” (from DRY principles etc?)
I totally agree with the DRY guidelines, but there are many ways to not repeat yourself. The issue with global variables (a variable that is not inside any class, just free-floating) are numerous:
It gets awkward when you want to include it in different targets
It's not part of any namespace, so it might overload another one from another library of file, it messes up the auto-completion, etc...
etc... you can find more documentation in this thread
Also, if I change to the 2nd example above, how would I then call the
right amount of those? Just replace “return exerciseInWorkout” with
the new function? Or would it be unchanged because the func is
contained in the struct?
So I understand correctly, what you actually want to create is a set of default generators for exercises that have a name and a max count of reps, and these should be available in the whole project (hence why you used global variables).
A good way to improve this code is by defining static generators, for instance you can update WorkoutExerciseGenerator to be
struct WorkoutExerciseGenerator {
let name: String
let maxReps: Int
// Add any other parameter you need to generate a good exercise
func generate() -> WorkoutExercise {
return WorkoutExercise(
name: name,
reps: Int(arc4random_uniform(maxReps))
)
}
// Generates a "Squat" workout
static var squat {
return WorkoutExerciseGenerator(name: "Squat", maxReps: 10)
}
// Generates a "Push Up" workout
static var pushUp {
return WorkoutExerciseGenerator(name: "Push Ups", maxReps: 5)
}
// Generates a "Viking Press" workout
static var vikingPress {
return WorkoutExerciseGenerator(name: "Viking Press", maxReps: 20)
}
}
Now that you have these specific generators, it looks like you also want to have a way to generate a whole workout. If that's the case, then you can simply create, in addition to what I wrote about, some objects to represent a workout and a workout generator.
/// This represents a whole workout that contains
/// multiple exercises
struct Workout {
let exercises: [WorkoutExercise]
}
/// This allows to dynamically creates a Workout
struct WorkoutGenerator {
// This is the "pool" of exercises from
// which it generates a workout (similar to your
// `exerciseBankArray`)
let exercisePool: [ExerciseGenerators]
// Min and max amount of workouts
let minCount: Int
let maxCount: Int
// Generates a workout from the generator
func generate() -> WorkoutGenerator {
let amount = Int(arc4random_uniform(maxCount - minCount)) + minCount
let exercises = (0..<amount).map { _ in
// Selects an exercise generator at random
let index = Int(arc4random_uniform(exercisePool.count))
// Generates a random workout exercise from this generator
return exercisePool[index].generate()
}
return Workout(exercises: exercises)
}
// Same thing here, you can use a static variable to create
// a "default" workout generator that contains the exercises
// you had inside your `exerciseBankArray`
static var default: WorkoutGenerator {
return WorkoutGenerator(
exercisePool: [.squat, .pushUp, .vikingPress],
minCount: 3,
maxCount: 6
)
}
}
Now that you have all of this, the only thing you need to do to create a totally random work-out according to your requirements is
let myWorkout = WorkoutGenerator.default.generate()
If you want to add more exercise types, just create more static ExerciseGenerator, and if you want to create different types of workouts (maybe with different exercise pools, some hard or some easy), just create additional static WorkoutGenerator. (Note that you don't need static, you can also just create the object directly in your VC).
Hope that helps!

Check duplicates properties on Swift array

I have a custom class called Place with 3 properties:
Name (String)
Category (String)
GeoPoint (CLLocationCoordinate2D)
I have an array of type [Place] of 100 objects and I want to check if there are duplicates on the GeoPoint property (just on this one).
How can I check duplicates of a specific property in an array of custom objects?
Thanks!
Although the accepted answer is good, I'd like to chip in.
There are two more ways to achieve what you want, and they both benefit from functionalities provided by SDK.
1 - Use Sets as Tj3n mentioned in a comment.
To achieve this you would need to make your Place conform to Hashable protocol.
class Place : Hashable {
var name = ""
var category = ""
var geoPoint: CLLocationCoordinate2D = CLLocationCoordinate2D()
var hashValue: Int {
get {
return geoPoint.longitude.hashValue &+ geoPoint.latitude.hashValue
}
}
}
func ==(lhs: Place, rhs: Place) -> Bool {
return lhs.geoPoint.latitude == rhs.geoPoint.latitude && lhs.geoPoint.longitude == rhs.geoPoint.longitude
}
The &+ operator in hashValue means "add, and don't crash at overflow". Using it is as straightforward as it can - let set = Set(yourArrayOfPlaces) - the set will contain only unique, in regard to geoPoint, places.
2 - Use KVC. While this is more of an Objective-C world, I find it a useful tool. To achieve this, you'd need to make Place inherit from NSObject. Then getting an array of unique places can be reduced to this one line :
let uniquePlaces = (yourPlacesArray as NSArray).value(forKeyPath: "#distinctUnionOfObjects.geoPoint")
You can do something like this:
var dict : [String : Int] = [:]
for place in arr {
if dict[place.GeoPoint] != nil { // Not in dictionary
if dict[place.GeoPoint] >= 1 { // If already there
return true // Duplicate
} else {
dict[place.GeoPoint]! += 1 // Increment instance
}
} else {
dict[place.GeoPoint] = 0 // Put in dictionary
}
}
return false // No duplicates
Where you loop through a [Place] array and check to see how many have the same GeoPoint. Then check to see if there's one there more than once.

How to remove common items from two struct arrays in Swift

In my app I have two struct arrays and I want to remove common items from one of them. My struct:
struct PeopleSelectItem {
var name = ""
var id = ""
var added = false
}
My arrays:
var people : [PeopleSelectItem] = []
var selectedPeople : [PeopleSelectItem] = []
I want to remove items from people array if they exist (compare by id) on selectedPeople array.
I tried several array filtering and converting to set but none of them worked. What can I do here?
Thanks!
Get an array of all ids in selectedPeople
let selectedPeopleIDs = selectedPeople.map(\.id)
Filter the items whose id is not in the array
let filteredPeople = people.filter { !selectedPeopleIDs.contains($0.id) }
If you know that people equal each other if the id is the same then you can conform your struct to the Equatable protocol and you can use the array filter method.
struct PeopleSelectItem : Equatable {
var name = ""
var id = ""
var added = false
}
func ==(lhs: PeopleSelectItem, rhs: PeopleSelectItem) -> Bool {
return lhs.id == rhs.id
}
func filterPeople() {
//swift 2, 3:
people = people.filter{!selectedPeople.contains($0)}
//swift older versions:
people = people.filter{!contains(selectedPeople, $0)}
}
If people might have a significant amount of entries, performance should be considered. So, instead of searching with an n^2 algorithm, you should make use of Swifts dictionary and the corresponding hash-search to find items.
If Id is unique for people then I would store them in a dictionary like:
var peopleDict: [String:PeopleSelectItem] = [:]()
You can easily convert from the array you have to this dictionary:
people.foreach {peopleDict[$0.id] = $0}
With this dictionary it's very easy to delete single entries:
selectedPeople.foreach {peopleDict.remove($0.id)}
Optionally to switch back to an array for people you just say:
let filteredPeople = peopleDict.values as [PeopleSelectItem]
Remarks
I assumed, that selectedPeople is smaller then the base of all people. If this is not the case, you should pu selectedPeople in a dictionary.
did I say I like this Spark like api? I think I do so.
I just wrote that code from top of my mind. If it is not completely syntactically correct let me know and I correct it.

Remove duplicate structs in array based on struct property in Swift

I have made a simple struct and implemented the Equatable protocol :
extension MyModelStruct: Equatable {}
func ==(lhs: NModelMatch, rhs: NModelMatch) -> Bool {
let areEqual = lhs.id == rhs.id
return areEqual
}
public struct MyModelStruct {
var id : String?
var staticId : String?
init(fromDictionary dictionary: NSDictionary){
id = dictionary["id"] as? String
...
}
Then in my project i get an array of [MyModelStruct], what i what to do is to remove all the MyModelStruct that have the same id
let val1 = MyModelStruct(id:9, subId:1)
let val2 = MyModelStruct(id:10, subId:1)
let val3 = MyModelStruct(id:9, subId:10)
var arrayOfModel = [val1,val2,val3]; // or set but i do not know how to use a set
var arrayCleaned = cleanFunction[M2,M3]
How can i make the cleanFunction ?
Can someone help please.
Thanks for all.
Xcode : Version 7.3.1
I really don't want people to just take an answer because it's the only one, that's why I'm showing you how you can use the power of sets. Sets are used wherever it doesn't make sense to have more than one, either it's there or not. Sets provide fast methods for checking whether an element is in the set (contains), removing an element (remove), combining two sets (union) and many more. Often people just want an array because they're familiar with it, but often a set is really what they need. With that said, here is how you can use a set:
struct Model : Hashable {
var id : String?
var hashValue: Int {
return id?.hashValue ?? 0
}
}
func ==(l: Model, r: Model) -> Bool {
return l.id == r.id
}
let modelSet : Set = [
Model(id: "a"),
Model(id: "hello"),
Model(id: "a"),
Model(id: "test")
]
// modelSet contains only the three unique Models
The only requirement for a type in order to be in a set is the Hashable protocol (which extends Equatable). In your case, you can just return the underlying hashValue of the String. If your id is always a number (which it probably is), you should change the type of id to be an Int, because Strings are much less efficient than Ints and it doesn't make sense to use a String.
Also consider storing this property somewhere, so that every time you receive new models, you can just do
modelSet.unionInPlace(newModels)
Use a Set instead of an Array.
I agree you are better off using a Set. You should be able to initialize the Set using an Array, e.g., var arrayOfModel: Set = [val1, val2, val3]. But because you are using a custom type, you will need to make sure that MyModelStruct conforms to hashable. This link has a good explanation.
But if you want to use an array then you need to change
let areEqual = rhs.id == lhs.id
to
let areEqual = rhs.id == lhs.id && rhs.subId == lhs.subId)
You need to modify your struct to have a subId property (and make the variables Int instead of String.
In answer to your question, yes you do need to iterative over the array.
If you extend the Array type with this function :
extension Array
{
func uniqueValues<V:Equatable>( value:(Element)->V) -> [Element]
{
var result:[Element] = []
for element in self
{
if !result.contains({ value($0) == value(element) })
{ result.append(element) }
}
return result
}
}
You'll be able to clean up duplicates using :
var arrayCleaned = arrayOfModel.uniqueValues({$0.id!})
without needing to make the structure Equatable.
Please note that this is not going to be efficient if your array is very large and you might want to consider filtering insertions into your array at the source if possible.

Resources