Merge JSON objects through unique field then print child node data - javascript

I have the following JSON:
data: {
questions: "[{"id":"vzDDWL3GQvJi","title":"This is question 1","type":"opinion_scale","ref":"data_q1","properties":[]},{"id":"okT0ieWJm74d","title":"This is question 2","type":"opinion_scale","ref":"data_q2","properties":[]},
answers: "[{"type":"number","number":2,"field":{"id":"vzDDWL3GQvJi","type":"opinion_scale","ref":"data_q1"}},{"type":"number","number":4,"field":{"id":"okT0ieWJm74d","type":"opinion_scale","ref":"data_q2"}},
createdDate: "2020-02-14T07:43:02.000000Z"
}
A tidy version of the above is:
Question (questions object)
title : "This is question 1"
ref" : "data_q1"
Answer (answers object)
ref" : "data_q1"
number : 2
So, for question 1 (with the ref: data_q1) the number (score) is 2.
What I'm trying to do, is to merge both questions answers together based on ref. I want to do this so that I can get the number. I.e. question and answer for data_q1 is 2.
I have the following:
// Get questions
var questionData = data.data.questions;
var questions = JSON.parse(questionData);
// get answers
var answerData = data.data.answers;
var answers = JSON.parse(answerData);
What I've tried:
var answersInfo = answers.map( function(order) {
if( answers.ref === "RefIDHere"){
var info = { "number": answers.number}
return info;
}
});
console.log(answersInfo);
However, the issue with the above is, in answers.ref ===, I don't know what to pass because the questions and answers haven't been mapped together yet.

Please try below solution.
finalResult = [];
questions.map( que =>
answers.map(ans => {
if(que.ref === ans.field.ref){
finalResult.push({question: que, answer: ans})
}
return finalResult;
}));
Now if you see finalResult has the question and respective answer and you can access the number.
Hope it helps.

1) Build an answer_number object which has ref as key and value as number from data.answers.
2) Use map over data.questions and add the number value from above.
Hope this helps.
const data = {
questions: [
{
id: "vzDDWL3GQvJi",
title: "This is question 1",
type: "opinion_scale",
ref: "data_q1",
properties: []
},
{
id: "okT0ieWJm74d",
title: "This is question 2",
type: "opinion_scale",
ref: "data_q2",
properties: []
}
],
answers: [
{
type: "number",
number: 2,
field: { id: "vzDDWL3GQvJi", type: "opinion_scale", ref: "data_q1" }
},
{
type: "number",
number: 4,
field: { id: "okT0ieWJm74d", type: "opinion_scale", ref: "data_q2" }
}
],
createdDate: "2020-02-14T07:43:02.000000Z"
};
const answers_number = data.answers.reduce(
(acc, curr) => Object.assign(acc, { [curr.field.ref]: curr.number }),
{}
);
const questions_count = data.questions.map(que => ({
...que,
number: answers_number[que.ref]
}));
console.log(questions_count);

Use filter:
let data = {
questions: '[{"id":"vzDDWL3GQvJi","title":"This is question 1","type":"opinion_scale","ref":"data_q1","properties":[]},{"id":"okT0ieWJm74d","title":"This is question 2","type":"opinion_scale","ref":"data_q2","properties":[]}]',
answers: '[{"type":"number","number":2,"field":{"id":"vzDDWL3GQvJi","type":"opinion_scale","ref":"data_q1"}},{"type":"number","number":4,"field":{"id":"okT0ieWJm74d","type":"opinion_scale","ref":"data_q2"}}]',
createdDate: "2020-02-14T07:43:02.000000Z"
}
let getQuestionAndAnswer = (data, ref) => {
let question = JSON.parse(data.questions).filter(v => v.ref === ref);
let answer = JSON.parse(data.answers).filter(v => v.field.ref === ref);
return {
question,
answer
};
};
console.log(getQuestionAndAnswer(data, "data_q1"));

Related

Best way to Filter and Map the combination of an array and an object [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 1 year ago.
Improve this question
What is the best way to map this?
I have an array of 5 stages here which is the initial stages array. However, I need to map the following newData array against these stages. field_4 should map against field-4. And field_5 should map against field-5. I have also attached how it should be returned. I realise this probably isn't the proper use of StackOverflow as it's more of a question, but I've been trying for a while with no luck. Any pointers in the right direction would be great.
const stages = [
{ key: 'field-one' },
{ key: 'field-two' },
{ key: 'field-three' },
{ key: 'field-four' },
{ key: 'field-five' },
]
const newData = {
field_four: 9,
field_five: 'D',
}
// should get
const stages =
[
{ key: 'field-one' },
{ key: 'field-two' },
{ key: 'field-three' },
{ key: 'field-four', value: 'D' },
{ key: 'field-five', value: 9 },
];
Just run a simple map function, compare the properties. If the required property is found, then append that object with a new value
const stages = [
{ key: "bank-feeds" },
{ key: "has-property" },
{ key: "other-loans" },
{ key: "industry-code" },
{ key: "time-in-business" }
];
const newData = {
trading_time_months: 9,
industry_code: "D"
};
const result = stages.map((stage) => {
const { key } = stage;
if (key === "industry-code") {
return { ...stage, value: newData.industry_code };
} else if (key === "time-in-business") {
return { ...stage, value: newData.trading_time_months };
} else {
return stage;
}
});
console.log(result);
To update your existing stages variable, you can use Object.entries on the newData and search in your stages for the appropriate stage. If found, assign a value.
Object.entries(newData).forEach(([key, value]) => {
const stage = stages.find((target) => target.key === key);
if (stage) {
stage.value = value;
}
});
Or you can go the other way around, if you don't need to adjust the existing array (preferable):
const newStages = stages.map((stage) => ({
...stage,
...(newData[stage.key] != null ? { value: newData[stage.key] } : {})
}));
and for a more readable last version (this does not clone the object inside the array):
const newStages = stages.map((stage) => {
if (newData[stage.key] != null) {
stage.value = newData[stage.key];
}
return stage;
});

How to simplify using array destructuring

eslint keeps showing me a prefer-restructuring error. However, I'm don't really know how array destructuring works and would love some help.
These are the two lines returning an error:
word.results.inCategory = word.results.inCategory[0];
// and:
word.results = word.results.filter(
(res) =>
Object.keys(res).includes('partOfSpeech') &&
Object.keys(res).includes('inCategory')
)[0];
Again, I'm not very knowledgable in this area, so any help on how to fix/simplify this specifically would be appreciated!
EDIT: Here is an example object for reference:
{
word: 'midrash',
results: [{
definition: '(Judaism) an ancient commentary on part of the Hebrew scriptures that is based on Jewish methods of interpretation and attached to the biblical text',
partOfSpeech: 'noun',
inCategory: ['judaism'],
typeOf: [ 'comment', 'commentary' ]
},
{
definition: 'something',
partOfSpeech: 'something',
}],
syllables: { count: 2, list: [ 'mid', 'rash' ] },
pronunciation: { all: "'mɪdrɑʃ" },
frequency: 1.82
}
To get the value of inCategory you should use the destructuring assignment as follow:
const obj = {
word: 'midrash',
results: {
definition: '(Judaism) an ancient commentary on part of the Hebrew scriptures that is based on Jewish methods of interpretation and attached to the biblical text',
partOfSpeech: 'noun',
inCategory: 'judaism',
typeOf: [ 'comment', 'commentary' ]
},
syllables: { count: 2, list: [ 'mid', 'rash' ] },
pronunciation: { all: "'mɪdrɑʃ" },
frequency: 1.82
}
let {results: {inCategory: category}} = obj;
//Now you can assign the category to word.results.inCategory
console.log(category);
For the filter approach, I suggest using the function Array.prototype.find
word.results = word.results.find(
(res) =>
Object.keys(res).includes('partOfSpeech') &&
Object.keys(res).includes('inCategory')
);
If you are already sure that your data structure is correct and both word.results.inCategory and word.results are arrays then this is how you do it:
const { results:{ inCategory: [inCategory] }} = word;
word.results.inCategory = inCategory;
// and:
const [results] = word.results.filter(
(res) =>
Object.keys(res).includes('partOfSpeech') &&
Object.keys(res).includes('inCategory')
);
word.results = results;
Of course in the second destructing when you filter you can just use find that allows you directly set the word.results without destructing:
word.results = word.results.find(
(res) =>
Object.keys(res).includes('partOfSpeech') &&
Object.keys(res).includes('inCategory')
);

Object name from array value

I have array with some values :
let typeArray = ["name", "strret", "car", "type"];
And I have one object :
let formDefinitionObject = {schema:{}}
I want the formDefinitionObject object be like :
let formDefinitionObject = {
schema: {
name: {
type: 'string',
title: 'Name',
required: true
},
strret: {
type: 'string',
title: 'Strret'
},
car: {
type: 'string',
title: 'Car'
},
type: {
type: 'string',
title: 'Type'
}
}
}
I want dynamically for each item in array to be object in formDefinitionObject.schema object. For example if i add one more item in array typeArray.push('country') to automatic add this object in formDefinitionObject.schema object.
Couldn't understand how required: true would fit in. Remaining things can be done as follows
var getFormDefinition = function(arr) {
function getSchemaObj(arr) {
return arr.map(d => ({
[d]: {
type: typeof(d),
title: d
}
}))
.reduce((a, b) => ({ ...a, ...b }))
}
var schemaObj = getSchemaObj(arr)
arr.push = function(...d) {
Object.assign(schemaObj, getSchemaObj(d))
return Array.prototype.push.call(arr, ...d)
}
return ({
schema: schemaObj
})
}
var typeArray = ["name", "strret", "car", "type"];
var result = getFormDefinition(typeArray)
console.log(result)
typeArray.push('nitish')
console.log(result)
Even though you did not clarify more how a field has to be required or does any field has to be as string, here's a solution based on what you provided so far.
The next snippet does the job and it has some explanations :
let typeArray = ['name', 'strret', 'car', 'type'],
formDefinitionObject = {
schema: {}
};
/** cycle through "typeArray" and populate "formDefinitionObject.schema" **/
typeArray.forEach(el => {
let currObj = {
type: 'string', /** YOU DID NOT SPECIY HOW TO DECIDE THE TYPE **/
title: el[0].toUpperCase() + el.substring(1), /** the title with the first letter being capitalized as you provided in the question. You can just use "el" instead of "el[0].toUpperCase() + el.substring(1)" if you'd like to print as it is **/
};
el === 'name' && (currObj['required'] = true); /** YOU DID NOT SPECIY HOW TO DECIDE IF A FIELD HAS TO BE REQUIRED. I just saw only the "name" as required so I did a basic (yet a stupid) check if the current element is "name" add a required to it **/
formDefinitionObject.schema[el] = currObj; /** add the current record to the "schema" attribute **/
});
console.dir(formDefinitionObject); /** printing the result **/
I'll be here if you answer our questions in the comments section.
Til then, hope I pushed you further.
You could use Proxy on the typeArray with the set trap so each time you push new value to the proxy array you can also add new property to your schema. This way you can simulate observer pattern.
You can also create some pattern to add additional properties like required for example name:prop1:prop2 but this way value is fixed to true.
let typeArray = ["name:required", "strret", "car", "type"];
let formDefinitionObject = {
schema: {}
}
let proxyArray = new Proxy(typeArray, {
set(obj, prop, value) {
if (prop != 'length') addToSchema(formDefinitionObject.schema, value);
return Reflect.set(...arguments);
}
})
function capitalize(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}
function addToSchema(schema, prop) {
const [name, ...params] = prop.split(':');
schema[name] = {
type: 'string',
title: capitalize(name)
}
params.forEach(param => schema[name][param] = true);
return schema;
}
proxyArray.reduce(addToSchema, formDefinitionObject.schema);
proxyArray.push('email:required:isEmail');
proxyArray.push('phone');
console.log(formDefinitionObject)
Update: You could use something like this name:prop1|value:prop2 to add property value other then true but if you don't specify value default is still true
let typeArray = ["name:required", "strret", "car", "type"];
let formDefinitionObject = {
schema: {}
}
let proxyArray = new Proxy(typeArray, {
set(obj, prop, value) {
if (prop != 'length') addToSchema(formDefinitionObject.schema, value);
return Reflect.set(...arguments);
}
})
function capitalize(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}
function addToSchema(schema, prop) {
const [name, ...params] = prop.split(':');
schema[name] = {
type: 'string',
title: capitalize(name)
}
params.forEach(param => {
const [key, value] = param.split('|');
schema[name][key] = value ? value : true
});
return schema;
}
proxyArray.reduce(addToSchema, formDefinitionObject.schema);
proxyArray.push('email:required:isEmail');
proxyArray.push('phone:default|123/555-333:required');
proxyArray.push('address')
console.log(formDefinitionObject)

Javascript using filter/includes on an array of objects

What is the best way to filter out data that exists within an object?
I was able to do use the below code when data was just an array of values but now I need to filter out any data where the item.QID exists in my array of objects.
Data Obj:
var data = [{
QID: 'ABC123',
Name: 'Joe'
},
{
QID: 'DEF456',
Name: 'Bob
}]
Snippet:
// I don't want to include data if this QID is in my object
this.employees = emp.filter(item =>!this.data.includes(item.QID));
From what I understand, includes only works on an array so I need to treat all of the QID values in my object as an array.
Desired Outcome: (assuming item.QID = ABC123)
this.employees = emp.filter(item =>!this.data.includes('ABC123'));
Result:
var data = [{
QID: 'DEF456',
Name: 'Bob'
}]
UPDATE:
Apologies, I left some things a little unclear trying to only include the necessary stuff.
// People Search
this.peopleSearchSub = this.typeahead
.distinctUntilChanged()
.debounceTime(200)
.switchMap(term => this._mapsService.loadEmployees(term))
.subscribe(emp => {
// Exclude all of the current owners
this.employees = emp.filter((item) => item.QID !== this.data.QID);
}, (err) => {
this.employees = [];
});
The above code is what I am working with. data is an object of users I want to exclude from my type-ahead results by filtering them out.
The question is a little ambiguous, but my understanding (correct me if I'm wrong), is that you want to remove all items from a list emp that have the same QID as any item in another list data?
If that's the case, try:
this.employees = emp.filter(item => !this.data.some(d => d.QID === item.QID))
some is an array method that returns true if it's callback is true for any of the arrays elements. So in this case, some(d => d.QID === item.QID) would be true if ANY of the elements of the list data have the same QID as item.
Try Object#hasOwnProperty()
this.employees = emp.filter(item =>item.hasOwnProperty('QID'));
You can use a for ... in to loop through and filter out what you want:
const data = [{
QID: 'ABC123',
Name: 'Joe'
},
{
QID: 'DEF456',
Name: 'Bob'
}]
let newData = [];
let filterValue = 'ABC123';
for (let value in data) {
if (data[value].QID !== filterValue) {
newData.push(data[value]);
}
}
newData will be your new filtered array in this case
You can use an es6 .filter for that. I also added a couple of elements showing the filtered list and an input to allow changing of the filtered value. This list will update on the click of the button.
const data = [{
QID: 'ABC123',
Name: 'Joe'
},
{
QID: 'DEF456',
Name: 'Bob'
}]
displayData(data);
function displayData(arr) {
let str = '';
document.getElementById('filterList').innerHTML = '';
arr.forEach((i) => { str += "<li>" + i.QID + ": " + i.Name + "</li>"})
document.getElementById('filterList').innerHTML = str;
}
function filterData() {
let filterValue = document.getElementById('filterInput').value;
filterText (filterValue);
}
function filterText (filterValue) {
let newArr = data.filter((n) => n.QID !== filterValue);
displayData(newArr)
}
<input id="filterInput" type="text" value="ABC123" />
<button type ="button" onclick="filterData()">Filter</button>
<hr/>
<ul id="filterList"><ul>

Scope: Manipulating global variables while passing containg objects through functions?

I am programming a small Node.js server, to handle surveys.
The surveys are stored on the server inside a global array.
When an user answers a question, the client will send the surveyID, the questionID and the answers to the server. On the server I then use Array.find() to determine the correct survey in the array, as well as the correct question inside the survey.
Now I add the answers to the object. So far, so good. Works.
But if I wanted to do the manipulation in another function, I cannot simply pass the found survey object into it, because then it's not the same anymore, but a new one, and manipulating it inside the sub-function would not change the global survey object - is that right?
So what I currently do is to pass the IDs into the sub-function and use Array.find in it again. That way it works.
My question is: Is that the correct way to do this? Or is there another way?
Sample Code:
var surveys = [{
id: 117171,
flag_anonym: true,
flag_live: true,
status: "open",
active_question: 0,
questions: [
{
id: 117192,
title: "Wie heißt die Hauptstadt von Deutschland?",
typ: "singlechoice",
answered_by_socket: [ ],
answered_by_user: [ ],
answers: [
{
id: 117188,
title: "Mainz",
votes_anonym: 11
},
{
id: 117189,
title: "Wiesbaden",
votes_anonym: 0
},
{
id: 117190,
title: "Berlin",
votes_anonym: 1
},
{
id: 117191,
title: "München",
votes_anonym: 0
}
]
}
]}];
function updateSurvey(data) {
var survey = surveys.find(function (s) {
return s.id === data.survey_id;
});
if (typeof survey !== "undefined") {
if(survey.flag_live) {
// live survey, question can only be answered if active
if (survey.active_question < survey.questions.length) {
var question = survey.questions[survey.active_question];
if (data.question_id === question.id) {
answerQuestion(data);
}
}
}else {
// question can always be answered
answerQuestion(data);
}
}
}
function answerQuestion(data){
// I have to do Array.find again
var survey = surveys.find(function (s) {
return s.id === data.survey_id;
});
var question = survey.questions.find(function (s) {
return s.id === data.question_id;
});
question.answered_by_socket.push(data.socket_id);
if (data.user_id) {
question.answered_by_user.push(data.user_id);
}
// update answers
question.answers = question.answers.map(function (a) {
for (var i = 0; i < data.answers.length; i++) {
var answer = data.answers[i];
if (a.id === answer.id) {
if (answer.checked) {
a.votes_anonym++;
}
return a;
}
}
return a;
});
}

Categories