Hello guys I have two objects
Questions
[
0:{
question:"favourite food", id:"1", question_type:"text",
sub_questions: {
0:{
question:"what is it's origin", parent_id:"1",
sub_question_id:"1" question_type:"text",
},
1:{
question:"how much does it cost", parent_id:"1",
sub_question_id:"2" question_type:"text",
}
}
},
1:{
question:"Dream car", id:"2", question_type:"text"
},
2:{
question:"favourite pet", id:"2", question_type:"text"
}
]
Answers
[
0:{
question_id:1, response: "Fufu", sub_question: false
},
1:{
parent_id:1, question_id:1, response: "Fufu originated from West Africa", sub_question: true
},
2:{
parent_id:1, question_id:2, response: "USD3", sub_question: true
},
3:{
question_id:3, response: "Tesla Model S", sub_question: false
}
4:{
question_id:3, response: "Cat (Nameless)", sub_question: false
}
]
I want to be able to append the answers to the right question fields (including the sub-questions)
Please all suggestions are welcomed and thanks in advance for helping, please note that I need the answer in pure javascript, no jquery
It would seem you will have to iterate through the questions array, and the attached sub_questions arrays, with array.prototype.forEach function and then using array.prototype.find function for each of item to find and attach a matching answer.
Please find the solution implemented in this link:
https://jsfiddle.net/jawLtz03/
var questions = [{
question: "favourite food",
id: "1",
question_type: "text",
sub_questions: [{
question: "what is it's origin",
parent_id: "1",
sub_question_id: "1",
question_type: "text",
},
{
question: "how much does it cost",
parent_id: "1",
sub_question_id: "2",
question_type: "text",
}
]
},
{
question: "Dream car",
id: "3",
question_type: "text"
},
{
question: "favourite pet",
id: "4",
question_type: "text"
}
];
var answers = [{
question_id: 1,
response: "Fufu",
sub_question: false
},
{
parent_id: 1,
question_id: 1,
response: "Fufu originated from West Africa",
sub_question: true
},
{
parent_id: 1,
question_id: 2,
response: "USD3",
sub_question: true
},
{
question_id: 3,
response: "Tesla Model S",
sub_question: false
},
{
question_id: 4,
response: "Cat (Nameless)",
sub_question: false
}
];
var innerHtml = "<ul>"
questions.forEach(function(question, index) {
innerHtml += `<li>Question ${index + 1}. ${question.question}?<br/>`;
const hasSub = question.sub_questions ? true : false;
question.answer = matchWithAnswer(question, false);
innerHtml += `Answer: ${question.answer.response}.`;
if (hasSub) {
innerHtml += "<p><ul>";
question.sub_questions.forEach(function(sub_question, sub_index) {
innerHtml += `<li>Sub-Question ${sub_index + 1}. ${sub_question.question}?<br/>`;
sub_question.answer = matchWithAnswer(sub_question, true);
innerHtml += `Answer: ${sub_question.answer.response}.</li>`;
});
innerHtml += "</ul></p>";
}
innerHtml += "</li>";
});
function matchWithAnswer(question, isSub = false) {
return answers.find(function(answer) {
if (isSub) {
return answer.sub_question == true && answer.parent_id == question.parent_id && answer.question_id == question.sub_question_id;
} else {
return answer.question_id == question.id;
}
});
}
innerHtml += "</ul>";
var div = document.getElementById('output');
div.innerHTML = innerHtml;
<h1>Q & A</h1>
<div id="output"></div>
Related
I have two dropdowns - where each dropdown should filter an objects key. The dropdowns should not exclude each other, or both values from dropdown should work indenpentedly from each other (ie both dropdown values does not need to be true for filtering).
When I select an item from the dropdown, I get one array with two objects, for each dropdown:
[
{
"name": "Type",
"value": [
"calibration"
],
"selected": [
{
"text": "calibration"
}
]
},
{
"name": "Function group",
"value": [
"1 - Test",
"2 - Programming"
],
"selected": [
{
"text": "1 - Test"
}
]
}
]
Above shows two objects, for the two different dropdowns - one with name "type" and one with "Function group".
The "value" in the object above is all of the dropdown items.
"selected" holds the selected item from the dropdown and the filtering should be based on that.In this case we have selected "calibration" and "Test".
The "type" dropdown should filter on the data "category" field while the "function group" should filter on "groupDescription" field. The data that needs to be filtered based on the mentioned keyes and selected values looks like this:
const mockData = [
{
operationDetails: {
id: '5119-03-03-05',
number: '41126-3',
description: 'Clutch wear, check. VCADS Pro operation',
category: 'calibration', //type dropdown
languageCode: 'en',
countryCode: 'GB'
},
functionDetails: {
groupId: 411,
groupDescription: 'Test', //function group dropdown
languageCode: '',
countryCode: ''
},
lastPerformed: '2021-02-22',
time: 20,
isFavorite: false
}
,
{
operationDetails: {
id: '5229-03-03-05',
number: '41126-3',
description: 'Defective brake pad',
category: 'calibration', ///type dropdown
languageCode: 'en',
countryCode: 'GB'
},
functionDetails: {
groupId: 411,
groupDescription: 'Programming', //function group dropdown
languageCode: '',
countryCode: ''
},
lastPerformed: '2020-01-22',
time: 20,
isFavorite: false
}
]
Playground with mock data and response example from dropdown here.
How to filter the data based on the values from the dropdown objects, for each key its responsible for?
It's not the prettiest code, but it does work. The one thing that you'd want to watch out for is the regex. It would be better to not have to parse and do a straight match like category, but if your cases are static then you should be able to figure out if this will work every time. It would also be nice to have a field key in filterDetails so you know which field to try to match in the actual data and you could program that in.
const filterDetails = [
{
name: "Type",
value: ["calibration"],
selected: [
{
text: "calibration",
},
],
},
{
name: "Function group",
value: ["1 - Test", "2 - Programming"],
selected: [
{
text: "Test",
},
],
},
];
const mockData = [
{
operationDetails: {
id: "5119-03-03-05",
number: "41126-3",
description: "Clutch wear, check. VCADS Pro operation",
category: "calibration", //type
languageCode: "en",
countryCode: "GB",
},
functionDetails: {
groupId: 411,
groupDescription: "Test", //function group
languageCode: "",
countryCode: "",
},
lastPerformed: "2021-02-22",
time: 20,
isFavorite: false,
},
{
operationDetails: {
id: "5229-03-03-05",
number: "41126-3",
description: "Defective brake pad",
category: "calibration", ///type
languageCode: "en",
countryCode: "GB",
},
functionDetails: {
groupId: 411,
groupDescription: "Programming", //function group
languageCode: "",
countryCode: "",
},
lastPerformed: "2020-01-22",
time: 20,
isFavorite: false,
},
];
console.log(
"filtered mockData: ",
mockData.filter(({ operationDetails, functionDetails }) => {
let groupDescriptionMatch = false;
let categoryMatch = false;
for (const details of filterDetails) {
if (
details.name === "Type" &&
details.selected[0].text === operationDetails.category
)
categoryMatch = true;
if (details.name === "Function group") {
let parsedGroup = details.selected[0].text.match(/[a-zA-Z]+/g);
if (parsedGroup[0] === functionDetails.groupDescription) {
groupDescriptionMatch = true;
}
}
}
return groupDescriptionMatch && categoryMatch;
})
);
I got an array (as result of a mongoDB query) with some elements like this:
{
"_id": "ExxTDXJSwvRbLdtpg",
"content": [
{
"content": "First paragraph",
"language":"en",
"timestamp":1483978498
},
{
"content": "Erster Abschnitt",
"language":"de",
"timestamp":1483978498
}
]
}
But I need to get just a single content field for each data array element, which should be selected by the language. So the result should be (assuming selecting the english content):
{
"_id": "ExxTDXJSwvRbLdtpg",
"content": "First paragraph"
}
instead of getting all the content data...
I tried to do it with find(c => c.language === 'en), but I don't know how to use this for all elements of the data array. Maybe it is also possible to get the data directly as a mongodb query??
You could iterate the array and replace the value inside.
var array = [{ _id: "ExxTDXJSwvRbLdtpg", content: [{ content: "First paragraph", language: "en", timestamp: 1483978498 }, { content: "Erster Abschnitt", language: "de", timestamp: 1483978498 }] }];
array.forEach(a => a.content = a.content.find(c => c.language === 'en').content);
console.log(array);
Version with check for content
var array = [{ _id: "ExxTDXJSwvRbLdtpg", content: [{ content: "First paragraph", language: "en", timestamp: 1483978498 }, { content: "Erster Abschnitt", language: "de", timestamp: 1483978498 }] }, { _id: "no_content" }, { _id: "no_english_translation", content: [{ content: "Premier lot", language: "fr", timestamp: 1483978498 }, { content: "Erster Abschnitt", language: "de", timestamp: 1483978498 }] }];
array.forEach(function (a) {
var language;
if (Array.isArray(a.content)) {
language = a.content.find(c => c.language === 'en');
if (language) {
a.content = language.content;
} else {
delete a.content;
}
}
});
console.log(array);
Given that _id and language are input variables, then you could use this aggregate command to get the expected result:
db.collection.aggregate([{
$match: {
_id: _id,
}
}, {
$unwind: '$content'
}, {
$match: {
'content.language': language,
}
}, {
$project: {
_id: 1,
content: '$content.content'
}
}])
var aobjs = [{
"_id": "ExxTDXJSwvRbLdtpg",
"content": [
{
"content": "First paragraph",
"language":"en",
"timestamp":1483978498
},
{
"content": "Erster Abschnitt",
"language":"de",
"timestamp":1483978498
}
]
}];
var result = aobjs.map(o => ({ id: o._id, content: o.content.find(c => c.language === 'en').content }));
This returns an object for each with just id and content. In this example, result would be:
[ { id: 'ExxTDXJSwvRbLdtpg', content: 'First paragraph' } ]
I am trying to create a category tree using the array of json objects below.
I want to set a category as a child of another category if its parent equals the id of the other, and I want the posts also to be a children of that category instead of having a separate field for posts, I'll add a flag field that if it is a category or not isParent.
It looks like its working alright, but as you may see, if a category has both category and post as child, it'll only show the categories. Another problem with that is if the post has a null value on its array, it will still push them as children.
What are the mistakes in my code, or is there a simpler or better solution to this?
var tree = unflatten(getData());
var pre = document.createElement('pre');
console.log(tree);
pre.innerText = JSON.stringify(tree, null, 4);
document.body.appendChild(pre);
function unflatten(array, parent, tree) {
tree = typeof tree !== 'undefined' ? tree : [];
parent = typeof parent !== 'undefined' ? parent : {
id: 0
};
_.map(array, function(arr) {
_.set(arr, 'isParent', true);
});
var children = _.filter(array, function(child) {
return child.parent == parent.id;
});
if (!_.isEmpty(children)) {
if (parent.id == 0) {
tree = children;
} else {
parent['children'] = children;
}
_.each(children, function(child) {
var posts = _.map(child.posts, function(post) {
return _.set(post, 'isParent', false);
});
child['children'] = posts;
delete child.posts;
unflatten(array, child);
});
}
return tree;
}
function getData() {
return [{
"id": "c1",
"parent": "",
"name": "foo",
"posts": [{
"id": "p1"
}]
}, {
"id": "c2",
"parent": "1",
"name": "bar",
"posts": [{
"id": "p2"
}]
}, {
"id": "c3",
"parent": "",
"name": "bazz",
"posts": [
null
]
}, {
"id": "c4",
"parent": "3",
"name": "sna",
"posts": [{
"id": "p3"
}]
}, {
"id": "c5",
"parent": "3",
"name": "ney",
"posts": [{
"id": "p4"
}]
}, {
"id": "c6",
"parent": "5",
"name": "tol",
"posts": [{
"id": "p5"
}, {
"id": "p6"
}]
}, {
"id": "c7",
"parent": "5",
"name": "zap",
"posts": [{
"id": "p7"
}, {
"id": "p8"
}, {
"id": "p9"
}]
}, {
"id": "c8",
"parent": "",
"name": "quz",
"posts": [
null
]
}, {
"id": "c9",
"parent": "8",
"name": "meh",
"posts": [{
"id": "p10"
}, {
"id": "p11"
}]
}, {
"id": "c10",
"parent": "8",
"name": "ror",
"posts": [{
"id": "p12"
}, {
"id": "p13"
}]
}, {
"id": "c11",
"parent": "",
"name": "gig",
"posts": [{
"id": "p14"
}]
}, {
"id": "c12",
"name": "xylo",
"parent": "",
"posts": [{
"id": "p15"
}]
}, {
"id": "c13",
"parent": "",
"name": "grr",
"posts": [{
"id": "p16"
}, {
"id": "p17"
}, {
"id": "p14"
}, {
"id": "p18"
}, {
"id": "p19"
}, {
"id": "p20"
}]
}]
}
<script src="//cdn.jsdelivr.net/lodash/3.10.1/lodash.min.js"></script>
Expected Output
So the expected output will be more like:
[
{
id: 'c1',
isParent: true,
children: [
{
id: 'c2',
isParent: true,
children: []
},
{
id: 'p1'
isParent: false
}
]
}
]
And so on..
Your code is very imperative. Try focusing on the "big picture" of data flow instead of writing code by trial-and-error. It's harder, but you get better results (and, in fact, usually it's faster) :)
My idea is to first group the categories by their parents. This is the first line of my solution and it actually becomes much easier after that.
_.groupBy and _.keyBy help a lot here:
function makeCatTree(data) {
var groupedByParents = _.groupBy(data, 'parent');
var catsById = _.keyBy(data, 'id');
_.each(_.omit(groupedByParents, ''), function(children, parentId) {
catsById['c' + parentId].children = children;
});
_.each(catsById, function(cat) {
// isParent will be true when there are subcategories (this is not really a good name, btw.)
cat.isParent = !_.isEmpty(cat.children);
// _.compact below is just for removing null posts
cat.children = _.compact(_.union(cat.children, cat.posts));
// optionally, you can also delete cat.posts here.
});
return groupedByParents[''];
}
I recommend trying each part in the developer console, then it becomes easy to understand.
I have made a small fidde that I think that is what you want.
http://jsfiddle.net/tx3uwhke/
var tree = buildTree(getData());
var pre = document.getElementById('a');
var jsonString = JSON.stringify(tree, null, 4);
console.log(jsonString);
pre.innerHTML = jsonString;
document.body.appendChild(pre);
function buildTree(data, parent){
var result = [];
parent = typeof parent !== 'undefined' ? parent : {id:""};
children = _.filter(data, function(value){
return value.parent === parent.id;
});
if(!_.isEmpty(children)){
_.each(children, function(child){
if (child != null){
result.push(child);
if(!_.isEmpty(child.posts)){
var posts = _.filter(child.posts, function(post){
return post !== null && typeof post !== 'undefined';
});
if(!_.isEmpty(posts)){
_.forEach(posts, function(post){
post.isParent = false;
});
}
result = _.union(result, posts);
delete child.posts;
}
ownChildren = buildTree(data, child);
if(!_.isEmpty(ownChildren)){
child.isParent = true;
child.children = ownChildren;
}else{
child.isParent = false;
}
}
});
}
return result;
}
EDIT: made a new fiddle to contain the isParent part you can find it here
While this problem looks simple, I can remember to have struggled achieving it in a simple way. I therefore created a generic util to do so
You only have to write maximum 3 custom callbacks methods.
Here is an example:
import { flattenTreeItemDeep, treeItemFromList } from './tree.util';
import { sortBy } from 'lodash';
const listItems: Array<ListItem> = [
// ordered list arrival
{ id: 1, isFolder: true, parent: null },
{ id: 2, isFolder: true, parent: 1 },
{ id: 3, isFolder: false, parent: 2 },
// unordered arrival
{ id: 4, isFolder: false, parent: 5 },
{ id: 5, isFolder: true, parent: 1 },
// empty main level folder
{ id: 6, isFolder: true, parent: null },
// orphan main level file
{ id: 7, isFolder: false, parent: null },
];
const trees = treeItemFromList(
listItems,
(listItem) => listItem.isFolder, // return true if the listItem contains items
(parent, leafChildren) => parent.id === leafChildren.parent, // return true if the leaf children is contained in the parent
(parent, folderChildren) => parent.id === folderChildren.parent // return true if the children is contained in the parent
);
console.log(trees);
/*
[
{
children: [
{
children: [{ data: { id: 3, isFolder: false, parent: 2 }, isLeaf: true }],
data: { id: 2, isFolder: true, parent: 1 },
isLeaf: false,
},
{
children: [{ data: { id: 4, isFolder: false, parent: 5 }, isLeaf: true }],
data: { id: 5, isFolder: true, parent: 1 },
isLeaf: false,
},
],
data: { id: 1, isFolder: true, parent: null },
isLeaf: false,
},
{ children: [], data: { id: 6, isFolder: true, parent: null }, isLeaf: false },
{
data: {
id: 7,
isFolder: false,
parent: null,
},
isLeaf: true,
},
]
*/
I did not check with your example as all cases are different, you however need to implement only 3 methods to let the algorithm build the tree for you:
If the item is a folder or a leaf (in your case just check if the children contain any non falsy item) i.e. listItem.posts.some((value)=>!!value)
if a parent contains the leaf child, (parent, child) => !!parent.posts.filter((val)=>!!val).find(({id})=>child.id === id)
if a parent contains the folder: optional if this is the same logic as for a leaf child.
This is my JSON output:
[
{
"Business": [
{
"id": "5739"
},
{
"userid": ""
},
{
"name": "Ben Electric"
},
{
"description": ""
},
{
"address": ""
},
{
"email": "*****#gmail.com"
},
{
"phone2": "050*****88"
},
{
"phone3": ""
},
{
"mobile": "050****88"
},
{
"opentimes": ""
},
{
"services": ""
},
{
"places": ""
},
{
"logo": null
},
{
"image": null
},
{
"video": ""
},
{
"owner_name": "Ben Brant"
},
{
"owners": "1"
},
{
"userpic": "http://graph.facebook.com/****/picture"
},
{
"circle": "3"
},
{
"fc": "0"
},
{
"rating_friends": ""
},
{
"rating_global": "3.3333"
},
{
"advice": ""
},
{
"subscription": "none"
}
]
},
{
"Business": [
{
"id": "5850"
},
{
"userid": ""
},
{
"name": "Bla Bla"
},
{
"description": ""
},
{
"address": ""
},
{
"email": "*****#gmail.com"
},
{
"phone2": ""
},
{
"phone3": ""
},
{
"mobile": "0*****995"
},
{
"opentimes": ""
},
{
"services": ""
},
{
"places": ""
},
{
"logo": null
},
{
"image": null
},
{
"video": ""
},
{
"owner_name": "Ben VBlooo"
},
{
"owners": "1"
},
{
"userpic": "http://graph.facebook.com/******/picture"
},
{
"circle": "3"
},
{
"fc": "0"
},
{
"rating_friends": ""
},
{
"rating_global": "2.0000"
},
{
"advice": ""
},
{
"subscription": "none"
}
]
},
{
"Info": {
"message": "No user for the business"
}
},
{
"OK": {
"message": "By Circle"
}
}
]
I'm trying to get the objects in javascript in this way but it doesnt work, should i loop through each Business object?? is there a way to access the real data objects directly?
Here's what I'm trying:
$.ajax({
type: 'POST',
url: 'BLABLA',
data: { BLABLA },
dataType: 'json',
success: function( resp ) {
if(resp.length == 0) {
$('.searchol').append('<li>No results found.</li>');
return;
}
$.each(resp, function(index, element) {
$('.searchol').append('Users Picture: '+element.Business.userpic);
But I cant seem to get to the object?
I just tried this code using your sample json like that
$.each(resp, function(index,element){
$.each(element, function(ind,ele){
if(ele.length){
$.each(ele,function(ind,ele){
if(ele.userpic)
console.log(ele.userpic)
})
}
})
})
"Business" is referring to an array (square bracket), so element.Business.userpic does not exist (element.Business[0].userpic exists though). Depending on what you want to achieve, you'll either have to loop through Business or access userpic of a particular array item.
Your business object is a array of object
"Business": [
{
"id": "5850"
},
Check this JSFiddle script on how to read that
Sample output
Picture: undefined (index):192
Picture: http://graph.facebook.com/****/picture
This will help you out
$.each(resp, function(index, element) {
$('.searchol').append('Users Picture: '+element.Business["userpic"]);
Your JSON is weird. Instead of :
Business : [
{ id : 'id1' }
{ name : 'name1' }
]
Business[0].id // access id
Business[1].name // access name
Where you have to remember where each attribute is in the array (or loop over the array to find it), you should have:
Business : {
id : 'id1',
name : 'name1'
}
Business.id // access id
Business.name // access name
If you can't change the JSON, you can use the following 2 methods to quickly get a property of Business:
var propMap = {
id : 0,
userid : 1,
name : 2 // etc
}
function getBusinessProp(business, prop) {
return business[propMap[prop]][prop];
}
// usage :
$('.searchol').append('Users Picture: '+ getBusinessProp(element.Business, 'userpic'));
If your array can be missing some items or the items can be in a different order for each business, then you need to iterate to find the property you're interested in:
function getBusinessProp(business, prop) {
for (var i=0; i<business.length; i++) {
if (business[i].hasOwnProperty(prop)) {
return business[i][prop];
}
}
}
// same usage, no need for the map anymore
The second method is probably better because it won't break if you change the order of the array or add new items in the array, etc and the performance boost given by using the map is probably not enough to justify the added maintenance cost.
i have a JSON array have the following data structure
"Jobs":
[
{ "id": "1", "JobTitle": "Engineer", "PID": "null" },
{ "id": "2", "JobTitle": "Project Manager", "PID": "null" },
{ "id": "5", "JobTitle": "Auditing Manager", "PID": "2" },
{ "id": "7", "JobTitle": "Auditor", "PID": "5" },
{ "id": "6", "JobTitle": "QA Manager", "PID": "5" },
{ "id": "3", "JobTitle": "QA", "PID": "6" },
{ "id": "4", "JobTitle": "Business Analyst", "PID": "2" }
]
i want to write a java script using Jquery and Knockoutjs (optional) to build a team structure (organization) with javascript and html, i have like 1000 record i have tried many recursive loops to handle it with no success.
the out put should be like this
<ul id="root">
<li>Engineer</li> //since their pid is null, then they are root nodes ( yeah not only root)
<li>Project Manager</li>
<ul>
<li>Auditing Manager</li>
<li>Business Analyst</li>
</ul>
and so on, it should handle many levels (depth), somebody will suggest DFS or BFS but i couldn't implement them successfully.
It's midnight, I'm tired, but I can not refuse this challenge. It may not be the fastest solution, but the result is good (http://jsfiddle.net/NbRzB/1/) :
function printNode(jobs, tree, id)
{
var html = '<li>' + jobs[id]['JobTitle'] + '</li>';
if(tree[id] instanceof Array)
{
html += '<li><ul>';
for(var i=0; i<tree[id].length; i++)
{
html += printNode(jobs, tree, tree[id][i]);
}
html += '</ul></li>';
}
return html;
}
var jobs =
[
{ 'id': '1', 'JobTitle': 'Engineer', 'PID': 'null' },
{ 'id': '2', 'JobTitle': 'Project Manager', 'PID': 'null' },
{ 'id': '5', 'JobTitle': 'Auditing Manager', 'PID': '2' },
{ 'id': '7', 'JobTitle': 'Auditor', 'PID': '5' },
{ 'id': '6', 'JobTitle': 'QA Manager', 'PID': '5' },
{ 'id': '3', 'JobTitle': 'QA', 'PID': '6' },
{ 'id': '4', 'JobTitle': 'Business Analyst', 'PID': '2' }
];
// tmp is used to build a better structure id => { attributes }
var tmp = {};
for(var i=0; i<jobs.length; i++)
{
tmp[jobs[i]['id']] =
{
'JobTitle' : jobs[i]['JobTitle'],
'PID' : jobs[i]['PID']
}
}
jobs = tmp;
// end - build better structure
// id => { child_id, child_id, ...}
var tree = {};
// { root_id, root_id, ...}
var root = [];
for(var id in tmp)
{
// no pid ? it is a root
if(jobs[id]['PID'] == 'null')
{
root.push(id);
}
else
{
// Add "id" to "jobs[id]['PID']"'s children
if(tree[jobs[id]['PID']] instanceof Array)
{
tree[jobs[id]['PID']].push(id);
}
else
{
tree[jobs[id]['PID']] = [ id ];
}
}
}
// recursive way
var html = '<ul id="root">';
for(var i=0; i<root.length; i++)
{
html += printNode(jobs, tree, root[i]);
}
html += '</ul>';
// output
$('body').append(html);