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

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;
});
}

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;
});

Vue computed array appends values instead of rewriting

I have an array called contactDetails that keeps a list of different contacts points (e.g. phone number, social media handle, etc) of the user. The available contact platforms are predefined in a list. I have a computed value that keeps track of which platforms the user has not added as a contact detail yet. The computed array is used in a select field for the user to choose from when creating a new contact detail.
new Vue({
el: '#app',
data() {
return {
platforms: [
{
text: 'WhatsApp',
value: 1,
},
{
text: 'Call',
value: 2,
},
{
text: 'Email',
value: 3,
},
{
text: 'LinkedIn',
value: 4,
},
{
text: 'TikTok',
value: 5,
},
{
text: 'Twitter',
value: 6,
},
],
contactDetails: [],
},
onAddContactDetails() {
var selectedPlatform = this.platforms.find(obj => {
return obj.value == this.platformId
})
var newContact = {
platform: selectedPlatform.value,
platformName: selectedPlatform.text,
channel: this.channel,
priority: this.contactPriorityId
}
this.contactDetails.push(newContact)
this.updatePriority(newContact, this.contactDetails)
this.platformId = null
this.contactPriorityId = null
this.channel = null
this.platformList = null;
this.isAddContact = false;
},
computed: {
platformList() {
var list = [];
if (this.contactDetails.length == 0) {
return this.platforms;
} else {
list = this.platforms;
for (var i = 0; i < this.contactDetails.length; i++) {
var id = this.contactDetails[i].platform;
for (var j = 0; j < this.platforms.length; j++) {
if (this.platforms[j].value == id) {
list.splice(j, 1)
}
}
}
}
return list;
},
This is how the dropdown looks like before adding a new contact detail.
However, my computed property updates, but instead of refreshing the list, the new list is appended onto the existing options, thus causing duplication.
The original list + the new list of contact details, which is supposed to be (full list - contacts that the user has already added)
I would like to know how to fix this, and if there is a better way of setting the options available left for the user in the dropdown menu. Thanks!
You are mutating this.platforms in the computed property; you should clone it first if you are going to mutate it:
list = this.platforms.slice()
I'm not sure what's causing the duplication though. You are only ever pushing to contactDetails and removing from platforms.
Your computed property can be simplified quite a bit:
computed: {
platformList() {
// Filter platforms
return this.platforms.filter(platform =>
// Include this platform if there is no contact detail using that platform
!this.contactDetails.some(contact =>
contact.platform === platform.value
)
)
}
}

Merge JSON objects through unique field then print child node data

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"));

How do i find an object with specific property within nested objects

For example, I have a Paths object
var Paths = {
path1: {
name: 'method1',
get: {
name: 'param1',
id: 1
},
post: {
name: 'param2',
id: 2
}
},
path2: {
name: 'method2',
get: {
name: 'param1',
id: 3
},
post: {
name: 'param2',
id: 4
}
}
};
I want to get the object based on the id.
I tried doing this _.find(Paths, {get:{id:1}}) But here id can also be in post object.
I need some help in solving this problem in lodash.
to find in object use _.pickBy
var res = _.pickBy(Paths, function(path) {
return path.get.id === 1 || path.post.id === 1;
});
for unknown key
var res = _.pickBy(Paths, function(path) {
return _.chain(path)
.values()
.some(function(val) {
return _.get(val, 'id') === 1;
})
.value();
});
Actually, your code is good as it is looking only inside get, not post. lodash also has matchesProperty iteratee which, in this case, could be done this way:
_.find(Paths, ["get.id", 1]);
Also, you can filter by custom functions:
_.find(Paths, function(o) { return o.get.id == 2 || o.post.id == 2; });

What is the JS pattern to get unique elements from nested array?

I have the following result from MongoDB.aggregate:
[{
_id: ObjectId(1),
_author: ObjectId(2),
comments: [
{
_author: ObjectId(2),
text: '...'
},
{
_author: ObjectId(3),
text: '...1'
},
{
_author: ObjectId(3),
text: '...2'
}...
]
}...]
I need to get all unique authors _author field from all elemnts (including nested):
var uniqAuthors = magicFunction(result) // [ObjectId(2), ObjectId(3)] ;
What is the best and compact way to make it with pure JS?
Array.prototype.reduce can help you:
var unique = result[0].comments.reduce(function(uniqueAuthors, comment) {
if (uniqueAuthors.indexOf(comment._author) === -1) {
uniqueAuthors.push(comment._author);
}
return uniqueAuthors;
}, []);
//Verify the author from document
if (unique.indexOf(result[0]._author) === -1) {
uniqueAuthors.push(result[0]._author);
}

Categories