Loop through multiple lists - swift

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)
}

Related

What is the best way in Swift 4+ to store a set of homogenous arrays for various types in a dictionary?

Consider a situation where we want to have a dictionary of arrays, with each array being a homogeneous collection of values of some type (which may be a struct or a primitive type). I'm currently using the ObjectIdentifier of the type defining it thusly:
let pInts : [UInt32] = [4, 6, 99, 1001, 2032]
let pFloats : [Float] = [3.14159, 8.9]
let pBools : [Bool] = [true, false, true]
let myDataStructure : [ObjectIdentifier : [Any]] = [
ObjectIdentifier(Float.self) : pFloats,
ObjectIdentifier(UInt32.self) : pInts,
ObjectIdentifier(Bool.self) : pBools
]
The issue here is that when traversing the data structure, Swift doesn't know that the objects in each list are homogeneous. Since swift is statically typed, I'm guessing it is not possible to typecast the [Any] lists using the ObjectIdentifier keys. Consider this traversal pseudocode:
for (typeObjId, listOfValuesOfSometype) in myDataStructure {
// do something like swap values around in the array,
// knowing they are homogeneously but anonymously typed
}
So, is there some metatype machinery I can concoct to represent this data structure in a way that does not anticipate the list of actual types that will have arrays in it?
I'm not exactly sure what you want to accomplish, Inside the dictionary loop the arrays will always be of type Any, but if you want to move items in the arrays around, you could just do that. Just reassign the array first to a var and then put it back in the dictionary.
If you do want to loop through the items of a specific type, then you could use the array helper function below.
func testX() {
let pInts: [UInt32] = [4, 6, 99, 1001, 2032]
let pFloats: [Float] = [3.14159, 8.9]
let pBools: [Bool] = [true, false, true]
var myDataStructure: [ObjectIdentifier: [Any]] = [
ObjectIdentifier(Float.self): pFloats,
ObjectIdentifier(UInt32.self): pInts,
ObjectIdentifier(Bool.self): pBools
]
// Swap the first 2 items of every array
for d in myDataStructure {
var i = d.value
if i.count > 1 {
let s = i[0]
i[0] = i[1]
i[1] = s
}
myDataStructure[d.key] = i
}
// Now dump all data per specific type using the array helper function.
for i: UInt32 in array(myDataStructure) {
print(i)
}
for i: Float in array(myDataStructure) {
print(i)
}
for i: Bool in array(myDataStructure) {
print(i)
}
}
func array<T>(_ data: [ObjectIdentifier: [Any]]) -> [T] {
return data[ObjectIdentifier(T.self)] as? [T] ?? []
}

filter dictionary of type [String: [object]] for specific key in object arrays

I'm showing users a tableview that looks like the contacts view in the sense that the data source is a dictionary of type [String: [User]] and so I'm showing section headers with the first letter of the Users name.
Now, I want to allow them to search by the users first name... but I can't get this to work.
These are the dictionaries... the friends will hold data like this ["A" :[Users whose first name start with an A]] and so on
var friends = [String: [User]]()
var filteredFriends = [String: [User]]()
This is my filtering code with searchText being the first name I want to search by
self.filteredFriends = self.friends.filter{friend in
return friend.firstName.lowercased().contains(searchText)}
What I want to do is make filteredFriends have all of the values of friends whose users first names start with the text.
How do I make this work with a dictionary like mine?
Thanks
More Info:
Class User {
var firstName: String?
init(name: String){
self.firstName = name
}
}
Sample scenario:
friends = ["A" : [User(name: "Anthony), User(name: "Arnold")], "B" : [User(name: "Barry")]]
filteredFriends = friends
searchText = "an"
Desired filteredFriends (end result) = ["A" : [User(name: "Anthony)]]
There's no simple way. You just have to cycle through the whole dictionary, like this:
// these are your initial conditions
class User {
let firstName: String
init(name: String) {
self.firstName = name
}
}
let friends : [String:[User]] =
["A" : [User(name: "Anthony"), User(name: "Arnold")],
"B" : [User(name: "Barry")]]
let searchText = "an"
// and here we go...
var filteredFriends = [String:[User]]()
for entry in friends {
let filt = entry.value.filter{$0.firstName.lowercased().hasPrefix(searchText)}
if filt.count > 0 {
filteredFriends[entry.key] = filt
}
}
Now filteredFriends contains the desired result.
Ideally, you would use map to transform the input dictionary to the output dictionary, by filtering each of its values (User arrays). Unfortunately, Dicationary.map returns [(Key, Value)] (an array of `(Key, Value) tuples), not a new Dictionary. I'll take the more conventional, iterative approach instead.
Note: For those who are about to circle jerk around using functional styles everywhere, who are about to suggest using reduce: no. Pull out a profiler, and look at how slow and power-consuming it is to generate dictionaries with reduce. Especially important on a mobile device with limited battery.
Here's how I would do it:
let allFriends = [String: [User]]()
var filteredFriends = [String: [User]]()
let searchText = "a".lowercased()
for (initial, allFriendsWithInitial) in allFriends {
if initial.lowercased() == searchText {
// don't even have to bother filtering `friends`
filteredFriends[initial] = allFriendsWithInitial
}
let filteredFriendsWithInitial = allFriendsWithInitial.filter{
$0.firstName.lowercased().contains(searchText)
}
if !filteredFriendsWithInitial.isEmpty {
filteredFriends[initial] = filteredFriendsWithInitial
}
}
print(filteredFriends)

How to sort dictionary by value?

My dictionary like this:
var items = [Int: [String]]()
var itemsResult = [Int: [String]]()
itmesResult stores the data downloaded from server.
and pass the data to items use of items = itmesResult
the value has 3 elements like ["Apple","/image/apple.png","29"]
and I want to sort the dictionary by first value which is Apple.
for (k,v) in (itemsResult.sorted(by: { $0.value[0] < $1.value[0] })) {
items[k] = v
}
The result of above code is not my expectation.
I would like to sort it alphabetically how can I do this?
Edit:
origin:
1:["Apple","/image/apple.png","29"]
2:["AAA","/image/aaa.png","29"]
3:["Banana","/image/banana.png","29"]
sorted:
2:["AAA","/image/aaa.png","29"]
1:["Apple","/image/apple.png","29"]
3:["Banana","/image/banana.png","29"]
I would like to sort it by first value.
So if I take your example, this does the trick:
var items = [Int: [String]]()
items[0] = ["Apple","/image/apple.png","29"]
items[1] = ["AAA","/image/aaa.png","29"]
items[2] = ["Banana","/image/banana.png","29"]
let itemResult = items.sorted { (first: (key: Int, value: [String]), second: (key: Int, value: [String])) -> Bool in
return first.value.first! < second.value.first!
}
print (itemResult)
The right thing to do is to use objects of course, and note that I'm not null checking the "first" object in each array, but this is not a problem to change.
Let me know if this is what you were looking for, the output is:
[(1, ["AAA", "/image/aaa.png", "29"]), (0, ["Apple", "/image/apple.png", "29"]), (2, ["Banana", "/image/banana.png", "29"])]
EDIT:
Also note, that this case doesn't actually "sort" the dictionary, because a dictionary is by definition not sorted, this creates an array of key-value objects sorted using the array indexes
Instead of saving these variable into an array of arrays, make them an array of dictionaries.
You can do this like so:
var dictionaries:[Dictionary<String, String>] = []
for item in items {
let dictionary = {"name": item[0], "url": item[1], "id" : item[2]}
dictionaries.append(dictionary)
}
You can get your sorted list of dictionaries like this:
dictionaries.sorted(by: { $0["name"] < $1["name"] })

How to extract a subset of a swift 3 Dictionary

I've looked through the methods here but I can't quite find what I'm looking for. I'm new-ish to Swift. I would like to extract a subset from a Dictionary based on a Set of key values, preferably without a loop.
For example, if my key Set is of type Set<String> and I have a Dictionary of type Dictionary<String, CustomObject>, I would like to create a new Dictionary of type Dictionary<String, CustomObject> that contains only the key-value pairs associated with the keys in the Set of Strings.
I can see that I could do this with for loop, by initializing a new Dictionary<String, CustomObj>(), checking if the original Dictionary contains a value at each String in the set, and adding key-value pairs to the new Dictionary. I am wondering if there is a more efficient/elegant way to do this however.
I'd be open to finding the subset with an Array of Strings instead of a Set if there is a better way to do it with an Array of keys.
Many thanks!
Swift 5 - You can do this very simply:
let subsetDict = originalDict.filter({ mySet.contains($0.key)})
The result is a new dictionary with the same type as the original but which only contains the key-value pairs corresponding to the keys in mySet.
Your assumption is correct, there is a more concise/swift-ish way to accomplish what you need.
For example you can do it via reduce, a functional programming concept available in Swift:
let subDict = originalDict.reduce([String: CustomObject]()) {
guard mySet.contains($1.key) else { return $0 }
var d = $0
d[$1.key] = $1.value
return d
}
Or, in two steps, first filtering the valid elements, and then constructing back the dictionary with the filtered elements:
let filteredDict = originalDict.filter { mySet.contains($0.key) }
.reduce([CustomObject]()){ var d = $0; d[$1.key]=$1.value; return d }
forEach can also be used to construct the filtered dictionary:
var filteredDict = [CustomObject]()
mySet.forEach { filteredDict[$0] = originalDict[$0] }
, however the result would be good it it would be immutable:
let filteredDict: [String:CustomObject] = {
var result = [String:CustomObject]()
mySet.forEach { filteredDict2[$0] = originalDict[$0] }
return result
}()
Dummy type:
struct CustomObject {
let foo: Int
init(_ foo: Int) { self.foo = foo }
}
In case you'd like to mutate the original dictionary (instead of creating a new one) in an "intersect" manner, based on a given set of keys:
let keySet = Set(["foo", "baz"])
var dict = ["foo": CustomObject(1), "bar": CustomObject(2),
"baz": CustomObject(3), "bax": CustomObject(4)]
Set(dict.keys).subtracting(keySet).forEach { dict.removeValue(forKey: $0) }
print(dict) // ["foo": CustomObject(foo: 1), "baz": CustomObject(foo: 3)]

How to remove duplicated objects from NSArray?

I have NSArray() which is include names but there's duplicated names how can i remove them ?
After parse query adding the objects to the NSArray and its duplicated
var names = NSArray()
let query = PFQuery(className: "test")
query.whereKey("receivers", equalTo: PFUser.currentUser()!.username!)
query.findObjectsInBackgroundWithBlock {
(objects, error) -> Void in
if error == nil {
self.names = objects!
let set = NSSet(array: self.names as [AnyObject])
print(objects!.count)
// count is 4
// database looks like this (justin , kevin , kevin , joe)
If your names are strings you could create NSSet from array and it will have only different names.
let names = ["John", "Marry", "Bill", "John"]
println(names)
let set = NSSet(array: names)
println(set.allObjects)
prints:
"[John, Marry, Bill, John]"
"[Bill, John, Marry]"
Update #1
With new information in question (code fragment) it may look like this
var set = Set<String>()
for test in objects as [Test] {
set.insert(test.sender)
}
self.names = Array(set)
To expand on John's answer, an NSSet will, by definition, only contain a single copy of each object that hashes to be equal. So, a common pattern is to convert the array to a set and back.
This will work for any object type that has a reasonable implementation of -hash and -isEqual:. As John shows, String is one such object.
You could also do it with pure Swift:
let arrayWithDuplicates = [ "x", "y", "x", "x" ]
let arrayWithUniques = Array(Set(arrayWithDuplicates)) // => [ "y", "x" ]
But, it looks like you're already working with NSArray, so I think the John's example is more applicable.
Also, as my example shows, be aware that the order of the final array is not guaranteed to be in the same order as your original. If you want that, I think you can use NSOrderedSet instead of NSSet.
Here is a more complicated way to approach this that works. You could just run through a loop of the array and create a new one from the original. For example:
var check = 0
let originalArray:NSMutableArray = ["x", "y", "x", "z", "y", "z"]
let newArray: NSMutableArray = []
println(originalArray)
for var int = 0; int<originalArray.count; ++int{
let itemToBeAdded: AnyObject = originalArray.objectAtIndex(int)
for var int = 0; int<newArray.count; ++int{
if (newArray == ""){
break;
}
else if ((newArray.objectAtIndex(int) as! String) != itemToBeAdded as! String){
}
else if ((newArray.objectAtIndex(int) as! String) == itemToBeAdded as! String){
check = 1
break
}
}
if (check == 0){
newArray.addObject(itemToBeAdded)
}
}
Basically I set a check var = 0. for every object in the originalArray, it loops through the newArray to see if it already exists and if it does the check var gets set to 1 and the object does not get added twice.