Querying an array of objects - coffeescript

Anyone have guidance on how to query an array of hashes in coffeescript?
For example, I have an array of hashes, each with a "name" and "setting":
[
{"name":"color", "setting":"red"},
{"name":"scale_min", "setting":"15"},
{"name":"scale_type", "setting":"linear"},
{"name":"x_axis_label", "setting":"Weeks"}
]
I want to find the element in this array where the hash "name" is "x_axis_label"
How can I easily do that with coffeescript?
I need some kind of value_for_key_in_object(key, object) function and figured if would be part of the lexicon...

I just hacked this up quickly:
data = [{"name":"color","setting":"red"},{"name":"scale_min","setting":"15"},{"name":"scale_type","setting":"linear"},{"name":"x_axis_label","setting":"Weeks"}]
find = (i for i in data when i.name is 'x_axis_label')[0]
alert(find.setting)
Demo

If you going to do this repeatedly, always looking for things where the name equals something, then you are better off converting this from an array of maps to just a map where the key is the name.
data = [
{"name":"color","setting":"red"}
{"name":"scale_min","setting":"15"}
{"name":"scale_type","setting":"linear"}
{"name":"x_axis_label","setting":"Weeks"}
]
myMap = {}
for row in data
myMap[row.name] = row.setting
alert(myMap['x_axis_label'])
Demo

I always prefer a 'multi language' solution over a 'idiomatic' solution. Thus you can to use Array.filter
data = [{"name":"color","setting":"red"},{"name":"scale_min","setting":"15"},{"name":"scale_type","setting":"linear"},{"name":"x_axis_label","setting":"Weeks"}]
find = (data.filter (i) -> i.name is 'x_axis_label')[0]
alert find.setting

If you happen to be using Underscore.js, you can use find:
xAxisLabel = _.find data, (datum) -> datum.name is 'x_axis_label'

Related

PhotoKit: How To Sort Photos Randomly

When fetching assets from the user's photo library, you must specify how you want the photos to be sorted. You do this by passing in a NSSortDescriptor(). There are a few completions, but I'm wondering if there's any way to pass in a "random sort".
I'm hoping there's something similar to the .sort function used like this:
var arr = [1, 2, 3]
arr = arr.sorted { Bool.random() } // Returns the array, sorted randomly
Is there any way I can achieve a "random sort" with NSSortDescriptor() ?
If not, is there any way to fetch random photos from a user's library (without fetching all of them and then sorting on those randomly) ?
I think you need to use a comporator in NSSortDescriptor for flexible sorting:
How to create an NSSortDescriptor using the comparator signature?

How can I search in arrays of integers with a compound MongoDB Atlas search query?

I am working on a function that helps me find similar documents, sorted by score, using the full-text search feature of MongoDB Atlas.
I set my collection index as "dynamic".
I am looking for similarities in text fields, such as "name" or "description", but I also want to look in another field, "thematic", that stores integer values (ids) of thematics.
Example:
Let say that I have a reference document as follows:
{
name: "test",
description: "It's a glorious day!",
thematic: [9, 3, 2, 33]
}
I want my search to match these int in the thematic field and include their weight in the score calculation.
For instance, if I compare my reference document with :
{
name: "test2",
description: "It's a glorious night!",
thematic: [9, 3, 6, 22]
}
I want to increase the score since the thematic field shares the 9 and 3 values with the reference document.
Question:
What search operator should I use to achieve this? I can input array of strings as queries with a text operator but I don't know how to proceed with integers.
Should I go for another approach? Like splitting the array to compare into several compound.should.term queries?
Edit:
After a fair amount of search, I found this here and here:
Atlas Search cannot index numeric or date values if they are part of an array.
Before I consider to change the whole data structure of my objects, I wanted to make sure that there is no workaround.
For instance, could it be done with custom analyzers?
I solved it by adding a trigger to my collection. Each time a document is inserted or updated, I update the thematic and other similar fields counterparts, e.g. _thematic, where I store the string value of the integers. I then use this _thematic field for search.
Here is a sample code demonstrating it:
exports = function (changeEvent) {
const fullDocument = changeEvent.fullDocument;
const format = (itemSet) => {
let rst = [];
Object.keys(itemSet).forEach(item => rst.push(itemSet[item].toString()));
return rst;
};
let setter = {
_thematic: fullDocument.thematic ? format(fullDocument.thematic) : [],
};
const docId = changeEvent.documentKey._id;
const collection = context.services.get("my-cluster").db("dev").collection("projects");
const doc = collection.findOneAndUpdate({ _id: docId },
{ $set: setter });
return;
};
I'm pretty sure it can be done in a cleaner way, so if someone post it, I'll switch the selected answer to her/his.
Another way to solve this is to make a custom analyser with character mapping that will replace each digit with its string counterpart. I haven’t tried this one tho. See https://docs.atlas.mongodb.com/reference/atlas-search/analyzers/custom/#mapping
Alternatives welcome!

How to get element property from array in Mongoose?

I have document structure like this in my schools collection
I am searching school by its code from array of codes School.findOne({ 'codes.code': '12345678' }) and I am getting my school but I also want to get code.type property from code object where my code is 12345678. Should i use javascript Array.find?
What you can do is selecting the matching array element by
School.find({ 'codes.code': '12345678' }, {"codes.$":1})
Otherwise, yes, you need to find the element in the array:
var myschool = School.find({ 'codes.code': '12345678' })[0]
myschool.codes.find(c => c.code == '12345678')

How compare the size of two arrays in mongo?

I have two fields with separate arrays that have comparable data in them.
The first has a Name, and an ID. The second has a nickname.
I want to make sure that the count of the two are the same. If they are not the same, I want to know the mongoID of that document.
How would I do this?
With MapReduce it would be possible. If your document looks like:
document: { array1: [ a, b], array2: [c] }
You could write map and reduce functions like:
map = function(){
if(this.array1.length!=this.array2.length)
emit(this_id,1);
}
reduce = function(key,values){ return key;}
For instance, to get the results inline:
db.foo.mapReduce(map,reduce,{out:{inline:1}}).results

Working with nested documents/arrays in Lithium and MongoDB

I'm new to both MongoDB and Lithium and I can't really find the "good way" of working with nested documents. I noticed that when I try
$user = Users::find('first' ... );
$user->somenewfield = array('key' => 'val');
what I get for "somenewfield" is a Document object. But there is also a DocumentArray class - what is the difference between them?
When I call
$user->save();
this results in Mongo (as expected):
"somenewfield" : {
"key": "value"
}
OK, but when I later want to add a new key-value to the array and try
$user->somenewfield['newkey'] = 'newval';
var_dump($user->somenewfield->to('array')); // shows the old and the new key-value pairs
$user->save(); // does not work - the new pair is not added
What is the correct way to adding a new array to a document using lithium? What is the correct way of updating the array/adding new values to the array? Shall I alywas give a key for the array value?
Thanks for the help in advance. I'm kinda stuck ... reading the documentation, reading the code ... but at some points it gets difficult to find out everything alone :)
Edit:
What I found at the end was that the way I shall use nested arrays is with $push and $pull:
Users::update(array('$push' => array('games' => (string) $game->_id)),
array(
'_id' => $this->user()->_id,
'games' => array('$ne' => (string) $game->_id)),
array('atomic' => false));
I think there are some quirks in handling subdocuments, you can try:
$somenewfield = $user->somenewfield;
$somenewfield->newkey = newvalue;
$user->somenewfield = $somenewfield;
$user->save();
Or the alternative syntax:
$user->{'somenewfield.newkey'} = $newvalue;
$user->save();
You should be able to find more examples in the tests (look in tests/data at any tests for Document).