I have the following form object:
{
"name":"form name",
"forms":[
{
"name":"form 1",
"forms":[
...
]
}
]
}
Forms can contain forms inside them.
The idea was to print all the form names.
I did the following:
forms.forEach(form -> {
console.log(form.name);
form.forms.forEach(f -> {
console.log(f.name);
f.forms.forEach(...);
})
});
I have no idea how many forms can be inside so how can I do this to infinity.
Thanks
You should use recursion. Could be something like this:
function logFormName(data) {
if (data.name) {
console.log(data.name)
}
if (data.forms) {
return data.forms.forEach((form) => logFormName(form))
}
return;
}
Use recursion
const pr = (data) => {
console.log(data.name);
data.forms.forEach((f) => pr(f));
};
// forms data
let forms = {
"name":"form name",
"forms":[
{
"name":"form 1",
"forms":[
]
}
]
}
// recursive function to iterate through each form
// and print name of the form
function printForms(forms) {
if(forms.hasOwnProperty("forms")){
console.log(forms.name);
// iterate over each form
forms.forms.forEach(form => {
// recursive call
printForms(form);
})
}
}
// call function to print name of each form
printForms(forms)
Related
I recently have spotted jscodeshift and leveraging it for refactoring.
But I can't find a way to add await before the node that I am seeking.
// AS-IS
userEvent.click(a);
await userEvent.click(b);
// TO-BE
await userEvent.click(a);
await userEvent.click(b);
This is how I query userEvent.click
const getFunctionCall = (obj:string, prop:string) => {
return source.find(j.CallExpression, {
callee: {
type: 'MemberExpression',
object: {
name: obj
},
property: {
name:prop
}
}
})
}
const clicks = getFunctionCall('userEvent', 'click');
clicks.forEach(i=> {
if (i.parentPath.value.type !== 'AwaitExpression') {
// code goes here
}
})
How can I add await before userEvent and let the rest code stay same in this case?
I checked out the document to find out how to build a statement or expression, but it wasn't understandable for me as I've just started to use this. I would appreciate if you can introduce any good materials for this library.
Thanks in advance!
It is simple, you need to wrap the existing CallExpression inside an await expression like this:
// Press ctrl+space for code completion
export default function transformer(file, api) {
const j = api.jscodeshift;
const root = j(file.source);
const body = root.get().value.program.body;
root.find(j.CallExpression, {
callee: {
object: {
name: "userEvent"
},
property: {
name: "click"
}
}
}).filter(path => {
return path.value.arguments[0].name === "a" ;
}).replaceWith(path => {
return j.awaitExpression(path.value)
});
return root.toSource();
}
I have already sorted the objects like the link below. My next step is to be able to use the sorted object to display on my render rather than just console.log().
I'm not sure if putting it back into an object is the approach. Would appreciate if anyone can help me out. Thanks
var byLikes = [
{ name: 'herman', Like: 5 },
{ name: 'tabitha', Like: 3 },
{ name: 'juags', Like: 1 },
{ name: 'ukiq', Like: 4 },
{ name: 'limau', Like: 10 },
{ name: 'kwe', Like: 6 }
];
byLikes.sort(sortByLike);
function sortByLike(a, b) {
var result = 0;
if (a.Like > b.Like) { result = 1; }
if (b.Like > a.Like) { result = -1; }
return result;
}
byLikes.forEach(function (cat) {
console.log(cat);
});
res.render('reload', { imglikes: sortedlikedhere, postername: sortednamehere });
byLikes can be passed to render method as below. In the template, it can be accessed via imglikes property.
res.render('reload', {
imglikes: byLikes,
postername: sortednamehere
});
Given a database scheme like the following:
{
"lessons": {
"subjectId1": {
"lessonId2": { ... },
"lessonId4": { ... },
"lessonId6": { ... }
},
"subjectId2": {
"lessonId1": { ... },
"lessonId3": { ... },
"lessonId5": { ... },
"lessonId7": { ... }
}
}
}
How do I retrieve the id(s) of any subject(s) that include a given lessonId?
I came up with a function like this:
const refWithParent = database.ref("lessons");
const snapshot = await refWithParent.orderByChild(lessonId).limitToLast(1).once("value");
let firebaseId = null;
if (snapshot.val() !== null) {
snapshot.forEach(entry => {
firebaseId = entry.key;
return true;
});
}
return firebaseId;
This does work but I can't create an index in the database as the children are dynamically created.
Is there any easier way to retrieve the id?
As I understood you want to retrieve one index of any subject that contains a lesson with a specific id. If so, you can add a node "contains" to each subject into which you will put a key/value of the form: lessonId: true.
{
"lessons":{
"subjectId1":{
"contains":{
"lessonId2":true,
"lessonId4":true,
"lessonId6":true
},
"lessonId2":{ ... },
"lessonId4":{ ... },
"lessonId6":{ ... }
},
"subjectId2":{
"contains":{
"lessonId1":true,
"lessonId3":true,
"lessonId5":true,
"lessonId7":true
},
"lessonId1":{ ... },
"lessonId3":{ ... },
"lessonId5":{ ... },
"lessonId7":{ ... }
}
}
}
And then make a query (assuming lessonID is a variable in your code):
refWithParent.orderByChild(`contains/${lessonID)`).equalTo(true).limitToFirst(1).once('value');
and then check the obtained snaphost as follows:
if (snapshot.val() !== null)
{
let subjectId = snapshot.key;
//TODO something with subjectId...
}
Note, you must always add corresponding pair to "contains" node when you add a lesson to a subject. The same with removing.
P.S. If you want to obtain IDs of all subjects that contain given lesson, then simply remove .limitToFirst(1) from the query. That's all.
In Nosql like firebase you can denormalise or form your database as you want. I would advise that you make all your lesson names have a concatenation of it's subject when you creating them in order to avoid your querying of the database.
The codes below can help in getting a unique lesson id with a concatenation of the subject name before you create them.
let newId = database.ref("lessons").ref.push().key; //<-- get a new unique id
let newlessonname = 'Subject2_' + newId;
So your db would look like this
{
"lessons": {
"subjectId1": {
"subjectId1_lessonId2": { ... },
"subjectId1_lessonId4": { ... },
"subjectId1_lessonId6": { ... }
},
"subjectId2": {
"subjectId2_lessonId1": { ... },
"subjectId2_lessonId3": { ... },
"subjectId2_lessonId5": { ... },
"subjectId2_lessonId7": { ... }
}
}
}
Alternatively
You can include your Subjectid in the object of each lesson like this
{
"lessons": {
"subjectId1": {
"lessonId2": {subjectid:"subjectId1" ... },
"lessonId4": { ... },
"lessonId6": { ... }
},
"subjectId2": {
"lessonId1": { ... },
"lessonId3": { ... },
"lessonId5": { ... },
"lessonId7": { ... }
}
}
}
In the below example, when using getData();, is it possible to access the data contained in its object map within a new function – ie. useData(); – that is an argument of getData();?
const getData = (useData) => {
const myData = {
0: { title: 'Hello' },
1: { title: 'World!' }
};
Object.keys(myData).map((item) => {
useData();
});
}
getData(console.log(
/**
* Somehow access data represented by `myData` and `item`
* in above object map along the lines of `myData[item].title`
*/
));
Do you want to achieve something like that?
You can call useData with some arguments inside map function. You can't call some function like console.log as argument to getData function in this case.
const getData = useData => {
const myData = {
0: { title: "Hello" },
1: { title: "World!" },
};
Object.keys(myData).map(item => {
useData(myData[item]);
});
};
getData(console.log);
Yes, that is the default behaviour. But you need to pass a function(console.log) instead of a function invocation(console.log()) and invoke it only later.
const getData = (useData) => {
const myData = {
0: { title: 'Hello' },
1: { title: 'World!' }
};
Object.keys(myData).map((item) => {
useData.apply(console, [item]);
useData.apply(console, [ myData[item].title ]);
//or useData(myData[item].title)
});
}
getData(console.log);
getData(console.log('something'));
is same as:
let x = console.log('something');
getData(x);
I have the following document:
{
"gameName":"Shooter",
"details":[
{
"submitted":1415215991387,
"author":"XYZ",
"subPlayer":{
"members":{
"squad1":[
{
"username":"John",
"deaths":0
}
]
},
"gameSlug":"0-shooter"
}
}
],
"userId":"foL9NpoZFq9AYmXyj",
"author":"Peter",
"submitted":1415215991608,
"lastModified":1415215991608,
"participants":[
"CXRR4sGf5AdvSjdgc",
"foL9NpoZFq9AYmXyj"
],
"slug":"1-shooterConv",
"_id":"p2QQ4TBwidjeZX6YS"
}
... and the following Meteor method:
Meteor.methods({
updateDeaths: function(gameSlug, user, squad) {
Stats.update({details.subPlayer.gameSlug: gameSlug}, ...}); // ???
}
});
My goal is to update the field deaths. The method has the argument user which is the user object (username = user.username). Furthermore, the argument squad is the squad name as string, e.g. squad1.
How can I do this?
Any help would be greatly appreciated.
You should use something like this:
db.collection.update(
{'userId':'foL9NpoZFq9AYmXyj', 'details.subPlayer.gameSlug':'0-shooter'},
{'$set':
{'details': [{
'subPlayer': {
'members': {
'squad1':[{'username':'John','death':1}]
}
}
}]
}
}
)
UPD:
Also, maybe a better way is to use find().snapshot().forEach(). But I don't know how Meteor supports it. Example:
Stats.find({'userId':'foL9NpoZFq9AYmXyj', 'details.subPlayer.gameSlug':'0-shooter' }).map(function (e) {
e.details.forEach(function (d) {
var squad = 'squad1';
d.subPlayer.members[squad].forEach(function (s) {
s.deaths = 10000;
});
});
Stats.update({'_id': e._id}, e);
});