In coffeescript, I'm trying to convert an array of objects into a dict, where one of the values of the object is taken as the key and all of the objects in the array with that value and up as being in an array in the dict linked to that key.
I have tried the code suggested here but this results in maximum one object per key. https://coffeescript-cookbook.github.io/chapters/arrays/creating-a-dictionary-object-from-an-array
I couldn't find any other examples that don't just result in one value per key.
So, for example (expanding on the example linked above), I have an array
cats = [
{
name: "Bubbles"
age: 1
},
{
name: "Sparkle"
favoriteFood: "tuna"
age: 2
},
{
name: "Felix"
age: 2
}
]
I want my result to be
catDict = {
1: [
{
name: "Bubbles"
age: 1
}
]
2: [
{
name: "Sparkle"
favoriteFood: "tuna"
age: 2
},
{
name: "Felix"
age: 2
}
]
}
catDict = {}
(catDict[cat.age]?.push(cat) or catDict[cat.age] = [cat]) for cat in cats
I used the accessor variant of the existential operator ?. to soak up null references. When a null reference is encountered the second half of the or kicks in to initialize the array.
It's shorter, but I'm not sure if it's more elegant...
Classic, coming up with a solution immediately after I give up and post the question on StackOverflow, but here's my solution:
addCatToDict = (cat, dict) ->
key = cat.age
if key of dict then dict[key].push(cat)
else dict[key] = [cat]
catDict = {}
for cat in cats
addCatToDict(cat, catDict)
interested to see more elegant solutions
Related
How could I sort an array of objects taking into account another array?
Example:
let sortArray = [90, 1000, 4520]
let notSortArrayObject = [ { id: 4520, value: 4 }, {id: 1000, value: 2}, {id: 90, value:10} ]
So I want an array equal notSortArrayObject but sortered by sortArray value
let sortArrayObject =[{id: 90, value:10} , {id: 1000, value: 2}, { id: 4520, value: 4 }]
Here's an approach. For each number in the "guide" array, find a match in the not sorted and add it to the sortedObjectArray. Note that this ignores any values which don't have matches in the other array.
var sortedObjectArray: [Object] = [] // whatever your object type is
for num in sortArray {
if let match = notSortArrayObject.first(where: {$0.id == num}) {
sortedObjectArray.append(match)
}
}
If you just want to sort by the id in ascending order, it's much simpler:
let sorted = notSortArrayObject.sorted(by: {$0.id < $1.id})
I noticed that sortArray is already sorted - you could just sort notSortArrayObject by id, but I assume that's not the point of what you're looking for.
So, let's say that sortArray = [1000, 4520, 90] (random order), to make things more interesting.
You can iterate through sortArray and append the values that match the id to sortArrayObject.
// To store the result
var sortArrayObject = [MyObject]()
// Iterate through the elements that contain the required sequence
sortArray.forEach { element in
// Add only the objects (only one) that match the element
sortArrayObject += notSortArrayObject.filter { $0.id == element }
}
So, I have the following objects:
struct Person {
let name: String
let birthday: String
}
let Bob = Person(name: "Bob", birthday: "11.12.1987")
let Tim = Person(name: "Tim", birthday: "11.12.1987")
let John = Person(name: "John", birthday: "01.02.1957")
let Jerry = Person(name: "Jerry", birthday: "17.12.2001")
And the following array:
let people = [Bob, Tim, John, Jerry]
My goal is to generate a dictionary from this array, with "birthday" for the key and the "Person" object itself as the value: [String: [Person]]. If there are equal keys the person should be added and form an array as a key. So the result will be the following:
dictionary = ["11.12.1987": [Bob, Tim], "01.02.1957": John, "17.12.2001": Jerry]
What is the best way to achieve this?
Cheers!
You could use Dictionary(grouping:by:) method together with mapValues,
let result = Dictionary(grouping: people, by: \.birthday)
print(result)
Output is,
["01.02.1957": ["John"], "17.12.2001": ["Jerry"], "11.12.1987":
["Bob", "Tim"]]
This would yield you a Dictionary of type Dictionary<String, [Person]>.
From, your question it seems like you want a Person if there is only one Person and array when there are multiple people. You would loose type in that situation and your type would be something like this, Dictionary<String, Any>. You could make small bit of modification to the above code to do that.
let result = Dictionary(grouping: people, by: \.birthday)
.mapValues { people -> Any in
if people.count == 1 {
return people.first!
}
return people
}
print(result)
The output would be similar to what you want,
["01.02.1957": "John", "17.12.2001": "Jerry", "11.12.1987": ["Bob",
"Tim"]]
What about a loop through the dictionary checking if date exists. Otherwise create key with date and assign value.
If key already exists append person to the actual key value:
var dictionary = [String: [Person]]()
// Loop through all person objects in array
for person in people {
// Check if date already exists
if dictionary[person.birthday] != nil {
// If already exists, include new person in array
var array = dictionary[person.birthday]
array!.append(person)
}
// If date does not exists, add new key with date and person
else {
dictionary[person.birthday] = [person]
}
}
I need to query Realm objects using a list of ids, id being the object's primary key. I have tried the following:
// Query a list of Dogs by their _ids
let doggoIds = ["1", "2", "3", "1", "2"]
realm.objects(Dog.self).filter("_id IN %#", doggoIds)
// Result: [Dog1, Dog2, Dog3]
The list of ids contains duplicates, but as you can see the result is a list of unique Dog objects.
I'm wondering if anyone can think of a different way to write this query so that we get a nice Results<Dog> collection from that list of ids that includes duplicates. Thanks!
Realm results contain unique objects so the easiest solution is to query for each dog and add the result to an array. CompactMap does this well as it filters out nil.
let listOfDogsToFind = ["Fido", "Dino", "Sport", "Fido", "Cupid"]
let dogArray = listOfDogsToFind.compactMap { dogName -> DogClass? in
if let result = realm.objects(DogClass.self).filter("dog_name == %#", dogName).first {
return result
}
return nil
}
for dog in dogArray {
print("dog name: \(dog.dog_name)")
}
assume Cupid doesn't exist, here's the output
dog name: Fido
dog name: Dino
dog name: Sport
dog name: Fido
Realm.objects().filter returns a list of "live", managed objects. It will not return duplicates because there is only one object matching the primary key. In order to accomplish what you want, you'll need to create an 'unmanaged' duplicate of each object and make your own array of those unmanaged objects map each returned instance into your own array.
A la:
let doggoIds = [1, 2, 3, 1, 2]
for managedMuttId in doggoIds {
if let managedMutt = realm.object(ofType: Dog.self, forPrimaryKey: managedMuttId) {
// unmanaged.append(Dog(value: managedMutt))
duplicateList.append(managedMutt)
}
}
I have a NSDictionary of NSDictionaries and I was wondering how do I sort the dictionaries by their value (Name)? The number will change from 0-n. Below is how the structure is setup:
let attribute = cumstomClass.Attribute!
attribute =
-Krqq2AWOqWKys0siTmc
Description: ""
Name: "B"
Value: ""
-KrqpSvSX7eKqYfvu5ZO
Description: ""
Name: "A"
Value: ""
Thanks in advance! :)
Since a Dictionary is inherently unsorted, you'll need to use an Array instead. Fortunately, this is easily done via functional techniques:
guard let dict = nsDict as? [String : [String : String]] else { /* handle the error */ }
let sorted = dict.sorted { ($0.value["Name"] ?? "") < ($1.value["Name"] ?? "") }
print(sorted)
This will get you an Array of tuples containing the keys and values of the respective dictionaries, sorted by "Name":
[(key: "KrqpSvSX7eKqYfvu5ZO", value: ["Name": "A", "Value": "", "Description": ""]),
(key: "Krqq2AWOqWKys0siTmc", value: ["Name": "B", "Value": "", "Description": ""])]
Alternately, you can simply get a sorted array of the String keys:
let sortedKeys = dict.sorted { ($0.value["Name"] ?? "") < ($1.value["Name"] ?? "") }.map {
$0.key
}
print(sortedKeys)
which outputs:
["KrqpSvSX7eKqYfvu5ZO", "Krqq2AWOqWKys0siTmc"]
This array of keys can later be used to dynamically look up objects from the dictionary at runtime when populating the collection view. This method will be likely to be more expensive performance-wise than the first approach due to all the dictionary lookups, but will result in lower memory usage if you also need to keep the original dictionary around in its original form for some reason.
Lets say I have dictionaries like below and wanted an array of red dogs. I figured I need to get an array of all the names of the type "dog" using the first dictionary, and then use the name key and the color to search the final dictionary to get ["Polly,"jake"]. I've tried using loops but can't figure out how to iterate through the dictionary.
var pets = ["Polly" : "dog", "Joey" : "goldfish", "Alex" : "goldfish", "jake" : "dog"]
var petcolor = ["Polly" : "red", "Joey" : "black", "Alex" : "yellow", "jake":red"]
The correct solution would seem to be to create a Pet struct (or class) and collate all of this information into a struct and build either an array or dictionary full of these values.
struct Pet {
let name: String
let type: String
let color: String
init(name: String, type: String, color: String) {
self.name = name
self.type = type
self.color = color
}
}
Now, let's build an array of these pets:
var goodPets = [Pet]()
for (petName, petType) in pets {
guard let petColor = petcolor[petName] else {
// Found this pet's type, but couldn't find its color. Can't add it.
continue
}
goodPets.append(Pet(name: petName, type: petType, color: petColor))
}
Now that we've filled out goodPets, pulling out any particular subset of Pets becomes very easy:
let redDogs = goodPets.filter { $0.type == "dog" && $0.color = "red" }
And although this answer looks like a lot of set up & legwork compared to other answers, the major advantage here is that once we build the goodPets array, any way we want to scoop pets out of there ends up being more efficient. And as we increase the number of properties the pets have, this becomes more and more true compared to the other answers.
If you'd rather store our model objects in a dictionary continuing to use the names as the keys, we can do that as well, but the filter looks a little bit stranger.
Building the dictionary looks mostly the same:
var goodPets = [String : Pet]()
for (petName, petType) in pets {
guard let petColor = petcolor[petName] else {
// Found this pet's type, but couldn't find its color. Can't add it.
continue
}
goodPets[petName] = (Pet(name: petName, type: petType, color: petColor))
}
But the filter is slightly different:
let redDogs = goodPets.filter { $0.1.type = "dog" && $0.1.color = "red" }
Note that in both cases, redDogs has the type [Pet], that is, an array of Pet values.
You can iterate through a dictionary like this:
for key in pets.keys() {
if pets[key] == "Dog" {
}
}
Or:
for (name, pet) in pets {
if pet == "Dog" {
}
}
nhgrif is probably correct about structure but, to answer the literal question:
let dogs = Set(pets.filter { $0.1 == "dog" }.map { $0.0 })
let redAnimals = Set(petscolor.filter { $0.1 == "red" }.map { $0.0 })
let redDogs = dogs.intersect(redAnimals)
Each filter is a block that operates on a (key, value) tuple, testing the value and ultimately creating a dictionary with only the matching (key, value) pairs. Each map then converts that filtered dictionary into an array by discarding the values and just keeping the keys.
Each array is turned into a set to support the intersect operation. The intersect then determines the intersection of the two results.