Passing hash data to javascript - javascript

I have a nested hash I've created from a previous question of mine Nested group query in Rails
The hash now being:
{14=>{11=>{21=>7, 22=>8}, 4=>{21=>7, 22=>4}}, 16=>{5=>{25=>2, 26=>1}, 11=>{26=>2, 25=>5}} }
I want to pass this data to javascript and I think it will be best to have key names for each attribute/model like so:
{ question_id: 14=>{ club_id: 11=>{ choice_id: 21=>7, choice_id: 22=>8} etc etc}
My question is, would this be a best practice to pass data in Rails to Javascript or would it be better to do this from a json variable from the controller.
If the approach I'm trying is fine, is there a way to set key names in a hash as my attribute names from my initial query?
Answer.where(:question_id => 14).group(:club_id, :choice_id).count
Thanks

There is no structure like that:
{ question_id: 14 => { club_id: 11 => { choice_id: 21 => 7, choice_id: 22 => 8 }
You're trying to associate a hash value with further values, which is an invalid structure. One way to achieve a semantic structure (meaning a structure which reflects the actual relationships of your counts and id's to each other) for your data so you can pass it to the browser via JSON would be to build a hash like the following:
{ questions: [
{
id: 14,
clubs: [
{
id: 11,
choices: [
{
id: 21,
count: 7
},
{
id: 22,
count: 8
}
]
}
]
}
]
}

Related

Using [Sequelize.Op.in] for json object arrays

I have a meta tag table which contains a tagId and a value.
I have an array of tagId,value objects
Eg : [{'tagId':2, 'value':33}, {'tagId':2, 'value':34}, {'tagId':1,
'value':34}, etc.. ]
My metaTag table consists of a virtual column which will return the {tagId,value} object for each entry in table. My question is how can I select rows with each {tagId, value} pair in the array.
In other words, I want to be able to do something like
[Sequelize.Op.in]:[{'tagId':2, 'value':33}, {'tagId':2, 'value':34}]
This doesn't work, however.
I might not have explained this well, English isn't my first language. Please ask if you need any clarification on the issue.
You can attain this by using Op.or. If I am not wrong you are trying
('tagId' = 2 and 'value' = 33) or ('tagId' = 2, 'value' = 34):
where: { [Sequelize.Op.or]: [{'tagId':2, 'value':33}, {'tagId':2, 'value':34}] }
You can add n number of values to the or array. As per your requirement.
if you want to do a in like this:
tagId in(2, 2) and value in (33, 34) then:
where: {'tagId':[2], 'value':[33, 34]}
You don't need the explicit Op.in for the array.
You can use there:
const myDeviceIds = [1, 2, 3, 4, 7, 9, 29];
const macroDevices = await MacroDevice.findAll({
where: {
deviceId: {
[Op.in]: myDeviceIds,
},
life: {
[Op.not]: null,
},
status: {
[Op.is]: null,
}
},
order: [
['id', 'DESC']
],
limit,
offset,
include: [
Macro,
Targets,
]
});

Javascript Joi Alternatives Array or Array of Objects

I'm confused about Joi alternatives. I can't figure out how the alternatives are invoked against the item to tb validated.
function validateCourse(course) {
const objectSchema = {
id: Joi.number().required(),
name: Joi.string().min(3).required()
};
const arraySchema = Joi.array().items(Joi.object(objectSchema)).required();
return Joi.alternatives(objectSchema, arraySchema).validate(course);
}
This works for the object but not for the array of objects.
[
{
"id": 10,
"name": "XY"
},
{
"id": 11,
"name": "JFK"
}
]
I'm not sure if my array schema is at fault or my use of alternatives?
It appears it is working! However the invalidation message returned by the Joi when validating an array is vague when compared to validating the object.
validating object : "name" length must be at least 3 characters long
validating array : "value" must be an object
Which is not completely useful as now I have to potentially check hundreds of items to find the one at fault.
But that's a different issue consider this closed!
There is nothing wrong with your schema. The error you are getting with the array is:
"value" must be an object, "value" at position 0 fails because
[child "name" fails because ["name" length must be at least 3 characters long]]
Which is right, your name key must have at least 3 characters.
And, as you asked, you can get the object(s) that failed from the error description or
you can also use any.error(err) in order to keep track of those objects.
For instance, if you want to know the array indexes that failed you can do:
const arraySchema = Joi.array().items(Joi.object(objectSchema)).required().error((errors) => {
const indexes = errors.map((err) => err.path[0]);
return new Error(`Indexes with error: ${indexes}`)
})
For this array of objects:
[
{
'id': 11,
'name': 'ABC'
},
{
'id': 22,
'name': 'ABC'
},
{
'id': 33,
'name': 'XY'
},
{
'id': 44,
'name': 'ABC'
},
{
'id': 55,
'name': 'XY'
},
]
The error will be:
Indexes with error: 2,4
You can access your error message by doing:
validationResult.error.message
Don't forget to use { abortEarly: false } in your validation:
Joi.alternatives(objectSchema, arraySchema).validate(course, { abortEarly: false })
Worked as described for arrays but then displayed nothing for non arrays so I made a small change :
const arraySchema =
Joi.array().items(Joi.object(objectSchema)).required().error((errors) =>
{
if (errors[0].path.length)
{
const indexes = errors.map((err) => err.path[0]);
if (indexes.length) return new Error(`Indexes with error: ${indexes}`);
}
return errors;
});
So if it's an array I return the array of bad indexes (as you suggested). But if it's an object I just return { errors } unchanged.

Populating a react-table with an object instead of a list

The documentation for the react-table library (https://github.com/react-tools/react-table#data) states:
"Simply pass the data prop anything that resembles an array or object."
However, the tables are rendered as expected when passing in an array of data, but when passing an object, I get the error:
"Invalid prop data of type object supplied to ReactTable, expected array."
An example data object looks like this:
const data = {
"entry1": {
name: 'Tanner Linsley',
age: 26,
friend: {
name: 'Jason Maurer',
age: 23,
}
}, "entry2": {
name: 'aTanner Linsley',
age: 26,
friend: {
name: 'aJason Maurer',
age: 23,
}
} };
Is this a problem with the structure of my object, or does it simply mean the library does not support the population via objects in this way?
Note: I prefer to maintain this data structure (which will become huge) as an object (dictionary) instead of an array so I can efficiently access elements by key for another use (outside of react-table).
The react-table library would expect an input like that:
const data = [{
name: 'Tanner Linsley',
age: 26,
friend: {
name: 'Jason Maurer',
age: 23,
}
},{
name: 'aTanner Linsley',
age: 26,
friend: {
name: 'aJason Maurer',
age: 23,
}
}];
I prefer to maintain this data structure
Is there any particular reason to that? However, if you really want to do it like that, you could apply the Object.values(...) (MDN Source) method to your data before passing it to the component. In that case you can manage it as you desire and the component will get the right data structure.
const convertedObject = Object.values(data);
But keep in mind that in this case, you will lose your keys entry1 and so on.

java script 2D array find unique array combinations

I need to know the best way to get following results
courseFrequency : [
{
'courses': [
'a.i'
],
'count' : 1
},
{
'courses': [
'robotics'
],
'count' : 2
},
{
'courses': [
'software engineering', 'a.i'
],
'count' : 2
},
{
'courses': [
'software engineering', 'a.i','robotics'
],
'count' : 1
}
]
from following json data.
arr = [
{
'courses': [
'a.i'
]
},
{
'courses': [
'robotics'
]
},
{
'courses': [
'software engineering', 'a.i'
]
},
{
'courses': [
'robotics'
]
},
{
'courses': [
'software engineering', 'a.i'
],
'courses': [
'software engineering', 'a.i','robotics'
]
}];
Basically i need to find out the unique courses and their frequency. What is the most optimal way to do that ?
const hash = {}, result = [];
for(const {courses} of arr){
const k = courses.join("$");
if(hash[k]){
hash[k].count++;
} else {
result.push(hash[k] = { courses, count : 1 });
}
}
Simply use a hashmap to find duplicates. As arrays are compared by reference, we need to join it to a string for referencing ( note that this will fail if a coursename contains the joining symbol ($))
There both of them are best for area relates to them.These concepts are heaving their own property and methods to accomplish a certain task like JSON used for data transfer and cross browsing aspect as the common type data value.Arrays are really good at storing ordered lists and ordering things while the cost of removing/splicing elements is a bit higher.
JSON is a representation of the data structure, it's not an object or an array.
JSON can be used to send data from the server to the browser, for example, because it is easy for JavaScript to parse into a normal JavaScript data structure.for doing an action on JSON data you need to convert it into an object which is also seamed some property like ARRAY.
Arrays are really good at storing ordered lists and ordering things while the cost of removing/splicing elements is a bit higher.
Relative link
Relative link

How to merge a heterogeneous array to a single document in MongoDb?

The MongoDb of my website stores a single document for each user. Each user will answer a couple of questionnaire forms during his visit. The forms are stored in an array, but since the documents don't overlap, a flat, single document would suffice. For analysis, I wish to produce a flat table of all the answers over all the forms.
Consider the following data structure:
{
"USER_SESSION_ID": 456,
"forms": [
{
"age": 21,
"gender": "m"
},
{
"job": "Student",
"years_on_job": "12"
},
{
"Hobby": "Hiking",
"Twitter": "#my_account"
}
]
},
{
"USER_SESSION_ID": 678,
"forms": [
{
"age": 46,
"gender": "f"
},
{
"job": "Bodyguard",
"years_on_job": "2"
},
{
"Hobby": "Skiing",
"Twitter": "#bodyguard"
}
]
}
The form-documents all look different and have no conflicting fields, so I would like to merge them, yielding a tabular, flat structure like this:
{ 'USER_SESSION_ID': 456, 'age': 21, 'gender': 'm', 'job': 'Student', ... 'Twitter': '#my_account' }
{ 'USER_SESSION_ID': 678, 'age': 46, 'gender': 'f', 'job': 'Bodyguard', ... 'Twitter': '#bodyguard' }
Using Python, this is a total no-brainer, looking like this:
for session in sessions: # Iterate all docs
for form in session['forms']: # Iterate all children
session.update(form) # Integrate to parent doc
del session['forms'] # Remove nested child
In MongoDb I find this quite hard to achieve. I am trying to use the aggregate pipeline, which I imagine should be suitable for this.
So far I helped myself by unwinding my datastructure, like this:
db.sessions.aggregate(
{
'$unwind': '$forms'
},
{
'$project': {
'USER_SESSION_ID': true,
'forms': true
}
},
{
'$group': {
'_id': '$USER_SESSION_ID',
'forms': <magic?!>
}
}
)
In the unwinding stage, I create a document with the parent's data for each child. This should be roughly equivalent to the double-for loop in my python code. However what I feel like I'm conceptually missing is the "Merge" accumulator upon grouping. In python, this is done with dict.update(), in underscore.js it would be _.extend(destination, *sources).
How do I achieve this within MongoDB?
Try the following which uses nested forEach() method calls of the find() cursor to iterate over the cursor result and get the object keys for the elements within the forms array using Object.keys():
db.sessions.find().forEach(function (doc){
doc.forms.forEach(function (e){
var keys = Object.keys(e);
keys.forEach(function(key){ doc[key] = e[key] });
});
delete doc.forms;
db.sessions.save(doc);
});
I played around with the aggregate pipeline for ages until I gave the mapReduce command a try. This is what I came up with:
db.sessions.mapReduce(
function () {
var merged = {};
this.forms.forEach(function (form) {
for(var key in form) {
merged[key] = form[key];
}
});
emit(this.USER_SESSION_ID, merged);
},
function () {},
{
"out": {"inline": true}
}
)
The mapping step combines the elements, since there is no single $merging operator available as an aggregation pipeline step. The empty reduce function is required. The out either writes to a different collection or just returns the result (inline, what I'm doing here).
It looks a lot like the method that chridam showed in his answer, but actually uses a projection. His version is much closer to the way that my python code works, but for what I'm trying to do a projection is fine and doesn't change the original set. Note that the python code does that, but not chaning the input collection is quite useful!

Categories