Struggling to loop through an array of objects - javascript

in particular I want to access answer's numerical value, so that later on to sum them up.
previously tried length, which is not working for objects.
here is my data:
const qData = [
{
id: 0,
question: "question 1",
answers: [
{ value: 1, text: "rafael" },
{ value: 2, text: "dontaelo" },
{ value: 3, text: "leonardo" },
{ value: 4, text: "michelangelo" }
]
},
{
id: 1,
question: "question 2",
answers: [
{ value: 1, text: "rafael" },
{ value: 2, text: "dontaelo" },
{ value: 3, text: "leonardo" },
{ value: 4, text: "michelangelo" }
]
}
];
export default qData;
I attempted to sum the answer values like so:
handleShowScore = () => {
var i, newScore;
var a = qData.answers;
for (i = 0; i < a.length; i++) {
newScore = newScore + a[i].value;
}
}

qData is an array and doesn't have the property answers, so qData.answers will not work and return undefined.
You first have to loop over the questions, then in each question you'll have to loop over the answers:
const qData = [{id:0,question:"question 1",answers:[{value:1,text:"rafael"},{value:2,text:"dontaelo"},{value:3,text:"leonardo"},{value:4,text:"michelangelo"}]},{id:1,question:"question 2",answers:[{value:1,text:"rafael"},{value:2,text:"dontaelo"},{value:3,text:"leonardo"},{value:4,text:"michelangelo"}]}];
let sum = 0;
for (const question of qData) {
for (const answer of question.answers) {
sum += answer.value;
}
}
console.log(sum);
You could do this reduce the amount of loops if you use flatMap. Pulling up the answers into one large array.
const qData = [{id:0,question:"question 1",answers:[{value:1,text:"rafael"},{value:2,text:"dontaelo"},{value:3,text:"leonardo"},{value:4,text:"michelangelo"}]},{id:1,question:"question 2",answers:[{value:1,text:"rafael"},{value:2,text:"dontaelo"},{value:3,text:"leonardo"},{value:4,text:"michelangelo"}]}];
const answers = qData.flatMap(question => question.answers);
let sum = 0;
for (const answer of answers) {
sum += answer.value;
}
console.log(sum);
Instead of using a for loop to sum the values you could also use reduce, which iterates over an array reducing it to a single value.
const qData = [{id:0,question:"question 1",answers:[{value:1,text:"rafael"},{value:2,text:"dontaelo"},{value:3,text:"leonardo"},{value:4,text:"michelangelo"}]},{id:1,question:"question 2",answers:[{value:1,text:"rafael"},{value:2,text:"dontaelo"},{value:3,text:"leonardo"},{value:4,text:"michelangelo"}]}];
const answers = qData.flatMap(question => question.answers);
const sum = answers.reduce((sum, answer) => sum + answer.value, 0);
console.log(sum);

The sample below does what you want, if I understood your question correctly
// Loop through all questions. Each question is stored in "q".
qData.forEach((q) => {
console.log('qData:', q);
​
// Make an array of all answer values
const answerValues = q.answers.map((a) => {
return a.value;
});
console.log('answerValues:', answerValues);
​
// Sum all answer values together
const totalValues = answerValues.reduce((a, b) => a + b, 0)
console.log('totalValues: ', totalValues);
});
If you want to have the sum of a specific ID
// Find specific ID.
const question = qData.find((q) => q.id === 0);
// Make an array of all answer values
const answerValues = question.answers.map((a) => {
return a.value;
});
console.log('answerValues:', answerValues);
// Sum all answer values together
const totalValues = answerValues.reduce((a, b) => a + b, 0)
console.log('totalValues: ', totalValues);

It's kind of a quiz, right? Let's assume my answers are:
const myAnswers = [{question: 'question 1', myAnswer:'rafael'},{question: 'question 2', myAnswer:'dontaelo'}]
I should have 1 point from the first question, and 2 from the second. We need to go through 2 loops: 1 to find the corresponding question, 1 to find the corresponding number of points:
const myPoints = myAnswers.map(answer => qData.find(question => question.question === answer.question).answers.find(possibleAnswer => possibleAnswer.text === answer.myAnswer).value)
Thats gives me [ 1, 2 ]. Now we need to to the sum with reduce:
const reducer = (accumulator, currentValue) => accumulator + currentValue;
const myScore = myPoints.reduce(reducer, 0);
I have 3 points ;-)

Related

Find a value by key from array of objects [duplicate]

This question already has answers here:
Find object by id in an array of JavaScript objects
(36 answers)
Closed last month.
I have an huge array of object and need to find the name of object by key, how can I do that with the optimized way.
const key = 2;
const arr = [ {id: 1, text: '1111'}, {id: 2, text: '2222'}, {id: 3, text: '333'},]
I need to return only the text '2222'
The original array have an 154 object thats why I need the most optimized way.
arr.forEach((obj) => {
Object.keys(obj).forEach((key) => {
console.log("key : " + key + " - value : " + obj[key]);
});
});
JavaScript has great array methods to do these kind of things. In your case I would use array.find to achieve what you're after. You can read more about it here
const key = 2;
const arr = [ {id: 1, text: '1111'}, {id: 2, text: '2222'}...]
const result = arr.find((item) => item.id === key).text;
console.log(result) // will print '2222'
You can do it using forEach or for loop
In term of optimisation for loop is the fastest one
var key = 2;
const arr = [ {id: 1, text: '1111'}, {id: 2, text: '2222'}, {id: 3, text: '333'},]
arr.forEach((obj) => {
if (obj.id == key) {
console.log(obj.text);
}
});
for (let i = 0; i < arr.length; i++) {
if (arr[i].id == key) {
console.log(arr[i].text)
}
}

Use array find() method instead of for loop

How can we use Array.find() method instead of for loop in this code ?
onLoadTickets() {
const ticketsReq = this.ticketService.getTickets();
const tariffsReq = this.tariffService.getTariffs();
forkJoin([ticketsReq, tariffsReq]).subscribe(results => {
const data = results[0];
const tariffResp = results[1];
this.tickets = data.requests;
for (let i = 0; i < this.tickets.length; i++) {
for (let j = 0; j < tariffResp.tariffs?.length; j++) {
if (tariffResp.tariffs[j].id == this.tickets[i].tariffId) {
this.tickets[i].tariff = tariffResp.tariffs[j]
}
}
}
});
}
Note :
Using find() method is not mandatory. I have to write this code with any array methods.
Edit :
I have used map() and includes() methods. my solution:
const tariffIds = tariffResp.tariffs.map((tariff: Tariffs) => tariff.id);
this.tickets.map((item) => {
if (tariffResp.tariffs === null || tariffResp.tariffs === undefined) {
return item;
}
if (tariffIds.includes(item.tariffId)) {
item.tariff = tariffResp.tariffs[tariffIds.indexOf(item.tariffId)];
}
return item;
});
This works but I'm not sure it's the best solution
Array.find() method returns the first element in the provided array that satisfies the provided testing function.
So, if in your case you only have one tariff against all the tickets then you can go ahead with Array.find() but if you have multiple tariff and multiple tickets then you can go ahead with Array.filter().
Demo with Array.find() :
const tickets = [{
tariffId: 1,
name: 'Ticket 1'
}, {
tariffId: 2,
name: 'Ticket 2'
}];
const tariffResp = {
tariffs: [{
id: 1
}]
};
const result = tickets.find((obj) => tariffResp.tariffs[0].id);
console.log(result);
Demo with Array.map() along with Array.filter() :
const tickets = [{
tariffId: 1,
name: 'Ticket 1'
}, {
tariffId: 2,
name: 'Ticket 2'
}, {
tariffId: 3,
name: 'Ticket 3'
}];
const tariffResp = {
tariffs: [{
id: 1
}, {
id: 2
}]
};
const result = tariffResp.tariffs.map((obj) => {
return tickets.filter((ticketObj) => obj.id === ticketObj.tariffId);
});
console.log(result);
this.tickets=data.request.map(x=>{
const obj:any=x;
obj.tariff=tariffResp.find(t=>t.id==x.tariffId)
return obj
})
You loop over data.request using map. map transform an array in another
First you create an object with the values of x
After you add a new propety "tariff" that is the "tariffResp" who has the "id" property equal to the property "tariffId" of x
Check find and map methods of an array

Javascript function to get 3 objects based by props

I am trying to make function to get top3 objects from an array based by props. My site can't load up so i think this function runs endlessly but i cant figure out why.
renderItems = (items) => {
let top3 = []
let tempArr = items
let allNumbers = []
while (top3.length < 3){
allNumbers = []
for(let i = 0; i < tempArr.length; i++){
allNumbers = [...allNumbers, tempArr[i].hearts]
}
const result = tempArr.filter(i => i.hearts === Math.max(...allNumbers))
top3 = [...top3, ...result]
let countOfdeleted = 0
for(let i = 0; i < result.length; i++){
tempArr.splice(result[i].id-countOfdeleted, 1)
countOfdeleted++
}
for(let i = 0; i < tempArr.length; i++){
tempArr[i].id = i
}
}
console.log(top3);
}
This answer is based on the assumption that 'items' is an array of objects and that each object in 'items' will have at-least 2 props namely 'id' and 'hearts'.
Further, there is no clarity on the significance of 'countOfdeleted' and 'tempArr'. Hence, it is assumed
that one needs to know how many elements of the 'items' array were not included (in the top3) as 'countOfdeleted'
that the remaining objects need to have their 'id' updated (based on index)
With the aforementioned assumptions, the below should implement the required logic:
const items = [
{ id: 0, hearts: 5 }, { id: 1, hearts: 4 }, { id: 2, hearts: 5 },
{ id: 3, hearts: 3 }, { id: 4, hearts: 5 }, { id: 5, hearts: 2 },
{ id: 6, hearts: 2 }, { id: 7, hearts: 1 }, { id: 8, hearts: 4 }
];
const getTop3 = (arr = items) => {
const maxVal = arr.reduce((fin, itm) => itm.hearts > fin ? itm.hearts : fin, 0);
const topAll = arr.filter(obj => obj.hearts === maxVal);
const sansTop3 = arr
.filter(obj => obj.hearts !== maxVal)
.map((obj, idx) => ({...obj, id: idx}));
console.log('countOfDeleted: ', arr.length - (topAll.length > 3 ? topAll.length : 3));
console.log('rest with updated-id (tempArr): ', sansTop3);
return topAll.length > 3 ? topAll.slice(0, 3) : [...topAll];
};
console.log('\ntop3:\n^^^^\n', getTop3());
Approach / Explanation
Find the 'maximum-value' (maxVal) based on the prop ('hearts')
Find all objects which have the props matching maxVal (stored in array 'topAll')
[Optional: Gather remaining 'items' elements and update their 'id' in 'sansTop3' array, to match the 'tempArr' in the question]
[Optional: Determine the number of 'items' deleted, to match countOfdeleted in the question]
If more than 3 elements have props ('heart') matching 'maxVal', return only 3; otherwise, return the all top-value element/s

how to count duplicate values object to be a value of object

how to count the value of object in new object values
lets say that i have json like this :
let data = [{
no: 3,
name: 'drink'
},
{
no: 90,
name: 'eat'
},
{
no: 20,
name: 'swim'
}
];
if i have the user pick no in arrays : [3,3,3,3,3,3,3,3,3,3,3,90,20,20,20,20]
so the output should be an array
[
{
num: 3,
total: 11
},
{
num: 90,
total: 1
},
{
num:20,
total: 4
}
];
I would like to know how to do this with a for/of loop
Here is the code I've attempted:
let obj = [];
for (i of arr){
for (j of data){
let innerObj={};
innerObj.num = i
obj.push(innerObj)
}
}
const data = [{"no":3,"name":"drink"},{"no":90,"name":"eat"},{"no":20,"name":"swim"}];
const arr = [3,3,3,3,3,3,3,3,3,3,3,20,20,20,20,80,80];
const lookup = {};
// Loop over the duplicate array and create an
// object that contains the totals
for (let el of arr) {
// If the key doesn't exist set it to zero,
// otherwise add 1 to it
lookup[el] = (lookup[el] || 0) + 1;
}
const out = [];
// Then loop over the data updating the objects
// with the totals found in the lookup object
for (let obj of data) {
lookup[obj.no] && out.push({
no: obj.no,
total: lookup[obj.no]
});
}
document.querySelector('#lookup').textContent = JSON.stringify(lookup, null, 2);
document.querySelector('#out').textContent = JSON.stringify(out, null, 2);
<h3>Lookup output</h3>
<pre id="lookup"></pre>
<h3>Main output</h3>
<pre id="out"></pre>
Perhaps something like this? You can map the existing data array and attach filtered array counts to each array object.
let data = [
{
no: 3,
name: 'drink'
},
{
no:90,
name: 'eat'
},
{
no:20,
name: 'swim'
}
]
const test = [3,3,3,3,3,3,3,3,3,3,3,90,20,20,20,20]
const result = data.map((item) => {
return {
num: item.no,
total: test.filter(i => i === item.no).length // filters number array and then checks length
}
})
You can check next approach using a single for/of loop. But first I have to create a Set with valid ids, so I can discard noise data from the test array:
const data = [
{no: 3, name: 'drink'},
{no: 90, name: 'eat'},
{no: 20, name: 'swim'}
];
const userArr = [3,3,3,3,3,3,3,3,7,7,9,9,3,3,3,90,20,20,20,20];
let ids = new Set(data.map(x => x.no));
let newArr = [];
for (i of userArr)
{
let found = newArr.findIndex(x => x.num === i)
if (found >= 0)
newArr[found].total += 1;
else
ids.has(i) && newArr.push({num: i, total: 1});
}
console.log(newArr);

retriving values from javascript object and then convert it to one object

I have a problem! I am creating an rating app, and I have come across a problem that I don't know how to solve. The app is react native based so I am using JavaScript.
The problem is that I have multiple objects that are almost the same, I want to take out the average value from the values of the "same" objects and create a new one with the average value as the new value of the newly created object
This array in my code comes as a parameter to a function
var arr = [
{"name":"foo","value":2},
{"name":"foo","value":5},
{"name":"foo","value":2},
{"name":"bar","value":2},
{"name":"bar","value":1}
]
and the result I want is
var newArr = [
{"name":"foo","value":3},
{"name":"bar","value":1.5},
]
If anyone can help me I would appreciate that so much!
this is not my exact code of course so that others can take help from this as well, if you want my code to help me I can send it if that's needed
If you have any questions I'm more than happy to answer those
Iterate the array with Array.reduce(), and collect to object using the name values as the key. Sum the Value attribute of each name to total, and increment count.
Convert the object back to array using Object.values(). Iterate the new array with Array.map(), and get the average value by dividing the total by count:
const arr = [{"name":"foo","Value":2},{"name":"foo","Value":5},{"name":"foo","Value":2},{"name":"bar","Value":2},{"name":"bar","Value":1}];
const result = Object.values(arr.reduce((r, { name, Value }) => {
if(!r[name]) r[name] = { name, total: 0, count: 0 };
r[name].total += Value;
r[name].count += 1;
return r;
}, Object.create(null)))
.map(({ name, total, count }) => ({
name,
value: total / count
}));
console.log(result);
I guess you need something like this :
let arr = [
{name: "foo", Value: 2},
{name: "foo", Value: 5},
{name: "foo", Value: 2},
{name: "bar", Value: 2},
{name: "bar", Value: 1}
];
let tempArr = [];
arr.map((e, i) => {
tempArr[e.name] = tempArr[e.name] || [];
tempArr[e.name].push(e.Value);
});
var newArr = [];
$.each(Object.keys(tempArr), (i, e) => {
let sum = tempArr[e].reduce((pv, cv) => pv+cv, 0);
newArr.push({name: e, value: sum/tempArr[e].length});
});
console.log(newArr);
Good luck !
If you have the option of using underscore.js, the problem becomes simple:
group the objects in arr by name
for each group calculate the average of items by reducing to the sum of their values and dividing by group length
map each group to a single object containing the name and the average
var arr = [
obj = {
name: "foo",
Value: 2
},
obj = {
name: "foo",
Value: 5
},
obj = {
name: "foo",
Value: 2
},
obj = {
name: "bar",
Value: 2
},
obj = {
name: "bar",
Value: 1
}
]
// chain the sequence of operations
var result = _.chain(arr)
// group the array by name
.groupBy('name')
// process each group
.map(function(group, name) {
// calculate the average of items in the group
var avg = (group.length > 0) ? _.reduce(group, function(sum, item) { return sum + item.Value }, 0) / group.length : 0;
return {
name: name,
value: avg
}
})
.value();
console.log(result);
<script src="http://underscorejs.org/underscore-min.js"></script>
In arr you have the property Value and in newArr you have the property value, so I‘ll assume it to be value both. Please change if wished otherwise.
var map = {};
for(i = 0; i < arr.length; i++)
{
if(typeof map[arr[i].name] == ‘undefined‘)
{
map[arr[i].name] = {
name: arr[i].name,
value: arr[i].value,
count: 1,
};
} else {
map[arr[i].name].value += arr[i].value;
map[arr[i].name].count++;
}
var newArr = [];
for(prop in map)
{
map[prop].value /= map[prop].count;
newArr.push({
name: prop,
value: map[prop].value
});
}
delete map;

Categories