Javascript function to get 3 objects based by props - javascript

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

Related

Multiple loop generate error for different entries

I made this function to generate custom fixture, but i have an issue if the players in rounds are more than 8. I tried to add 10 playes instead of 8, and the loop return me a wrong result.
let teams = [
{ id: "Team1" },
{ id: "Team2" },
{ id: "Team3" },
{ id: "Team4" },
{ id: "Team5" },
{ id: "Team6" },
{ id: "Team7" },
{ id: "Team8" },
{id: "Team9" },
{id: "Team10" }
]
// Total rounds
let totRounds = 3
// Array with rounds
let rounds = []
for (let i = 0; i < totRounds; i++) {
// This loop add an array on enemyes teams enemies["team2,..."]
teams.forEach(team => team.enemies = teams.filter(enemy => enemy !== team));
// Empty Array for first round
const matches = [];
// Empty Array for second round ( return )
const matches_return = [];
while (teams.some(team => team.enemies.length)) {
const playing = [];
const playing_return = []
for (const team of teams) {
//debugger
if (playing.includes(team)) continue;
const enemy = team.enemies.find(enemy => !playing.includes(enemy));
if (!enemy) continue;
team.enemies.splice(team.enemies.indexOf(enemy), 1);
enemy.enemies.splice(enemy.enemies.indexOf(team), 1);
playing.push(team, enemy);
playing_return.push(enemy, team);
}
if (playing.length) matches.push(playing.map(t => t.id))
if (playing_return.length) matches_return.push(playing_return.map(t => t.id))
}
// Merge 2 arrays
const totalMatches = matches.concat(matches_return)
rounds.push(totalMatches);
}
console.log(rounds);
This work with 8 players, but with 10 or more not. I tried to made some changes but they didn't work.

Struggling to loop through an array of objects

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

Get combination of all elements in JavaScript Object

I'm trying to write a program that generates every single item based on my JSON structure and giving each combination an individual number. I found a function on here that so far does its job listing out every combination for me, however I cannot decipher the code to the point I actually even understand what it does. It gives me all the items, listen like Key : Value but honestly I have no idea what part of the code does what and I cannot access it in order to build in my giving them unique numbers. This is the code that I found on here (lost the thread to it):
function getCartesian(object) {
return Object.entries(object).reduce((r, [k, v]) => {
var temp = [];
r.forEach(s =>
(Array.isArray(v) ? v : [v]).forEach(w =>
(w && typeof w === 'object' ? getCartesian(w) : [w]).forEach(x =>
temp.push(Object.assign({}, s, { [k]: x }))
)
)
);
return temp;
}, [{}]);
}
var input = { bookSet: { book: ["book1", "book2", "book3"], title: ["title1", "title2"], author: ["author1"], publisher: ["publisher1"] } },
cartesian = { eachBook: getCartesian(input.bookSet) };
It's just written in a too advanced syntax for me to remotely understand where I have to insert myself to make any calculations. I guess what I'm asking for would be either an explanation or a somewhat more understandable and modifyable code. I definitely need to run through all elements like this is doing and the output looks great from what I could tell so far, I just need to somehow calculate the keys and have an output of a number derived of each object's elements' combined keys.
An example would be book 243 for title 2, author 4 and publisher 3. I hope anyone can make sense of this. Thanks a lot!
EDIT: Included my own data and desired output. The combinations I displayed don't need to make sense.
var Product = {
json: { Product : {
assortment: [
{
name: "Yoghurt",
Flavor: ["natural", "honey", "stracciatella"],
Kind: ["greek", "soy"],
},
{
name: "Sauce",
},
{
name: "Milk Drink",
}
],
Brand: ["Oatly", "Dannon"],
Containment: ["Cup", "Jar"]
}}};
My output I'd like to generate the combinations of all of those and ultimately calculate the numbers on the right in the following screenshot
Given C_1 and C_2 two sets
The cartesian product of C_1 and C_2
is given by C_1 x C_2 = {(c_1_i,c_2_j) for c_1_i in C_1, c_2_j in C_2}
You can build C_1 x C_2 x C_3 by considering (C_1 x C_2) (that you calculated before) and "adjoining" each elem of C_3 to a tuple of C_1 x C_2
And so forth
const cartesianProduct = (C, D) => {
const res = []
C.forEach(c => {
D.forEach(d => {
// in case the tuple contains only one element (the initialization)
// make the elmeent into a tuple
const tuple = Array.isArray(c) ? c : [c]
res.push([...tuple,d])
})
})
return res
}
const nCartesianProduct = (Cs_n) => {
// we adjoin each elem of C_i and we "grow"
return Cs_n.reduce((res, C_i) => cartesianProduct(res, C_i))
}
console.log(nCartesianProduct([['b1', 'b2', 'b3'], ['t1', 't2'], ['a'], ['p']]))
Here is my attempt to lay in a simple terms:
Lets assume an example of
const sets = [ [1], [1,2], [1,2,3] ]
Possible combinations may be logged as following:
1 1 1 1 2 1
1 1 2 -> 1 2 2
1 1 3 1 2 3
Lets think of it as a clock, where last row will increase the value of previous row, once it reaches its maximum. In another words: lets increase i position of the last row and when over the limit -> drop it to zero and increase sibling instead, where if sibling is over the top -> repeat.
Consider the following code:
let sets = [[1,2], [1,2,3], [1,2,3,4], [1,2,3,4,5] ];
let state = sets.map( () => 0 );
console.log(sets, state);
function handleIncreament(i){
if( state[i] >= sets[i].length){
if(i-1 < 0) {
console.log('end of the row');
return false;
}
state[i] = 0;
state[i-1] += 1;
return handleIncreament(i-1);
}
else {
return true;
}
}
while( handleIncreament(state.length - 1) ){
console.log( state );
state[state.length - 1]++;
}
Above will log as follows:
(4) [Array(2), Array(3), Array(4), Array(5)] (4) [0, 0, 0, 0]
(4) [0, 0, 0, 0]
(4) [0, 0, 0, 1]
(4) [0, 0, 0, 2]
(4) [0, 0, 0, 3]
(4) [0, 0, 0, 4]
(4) [0, 0, 1, 0]
(4) [0, 0, 1, 1]
(4) [0, 0, 1, 2]
...
(4) [1, 2, 3, 4]
end of the row
4
With that lets apply it to your example:
const test = { bookSet: { book: ["book1", "book2", "book3"], title: ["title1", "title2"], author: ["author1"], publisher: ["publisher1"] } };
sets = Object.values(test.bookSet);
state = sets.map( () => 0 );
console.log(sets, state);
const matches = [];
while( handleIncreament(state.length - 1) ){
const match = sets[0][state[0]] + ' ' + sets[1][state[1]] + ' ' + sets[2][state[2]] + ' ' + sets[3][state[3]];
matches.push( match );
state[state.length - 1]++
}
console.log(matches);
And expect to get the following:
["book1 title1 author1 publisher1", "book1 title2 author1 publisher1", "book2 title1 author1 publisher1", "book2 title2 author1 publisher1", "book3 title1 author1 publisher1", "book3 title2 author1 publisher1"]
You could take the above data without superfluous parts and simplify the result by creating a flat array of the nested properties.
The numbers of the result picture are not incuded, because of the missing relation of each value to the given data set.
function getCartesian(object) {
return Object.entries(object).reduce((r, [k, v]) => {
var temp = [];
r.forEach(s =>
(Array.isArray(v) ? v : [v]).forEach(w =>
(w && typeof w === 'object' ? getCartesian(w) : [w]).forEach(x =>
temp.push(Object.assign({}, s, { [k]: x }))
)
)
);
return temp;
}, [{}]);
}
var data = {
assortment: [
{
name: "Yoghurt",
Flavor: ["natural", "honey", "stracciatella"],
Kind: ["greek", "soy"],
},
{
name: "Sauce",
},
{
name: "Milk Drink",
}
],
Brand: ["Oatly", "Dannon"],
Containment: ["Cup", "Jar"]
},
result = getCartesian(data)
.map(({ assortment: { name, Flavor = '', Kind = '' }, d = '', Brand, f = '', Containment, h = '', i = '', j = '' }) =>
[name, Flavor, Kind, d, Brand, f, Containment, h, i, j]);
console.log(result.length);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

How to convert an unorganized array into an grouped array by id

I'm trying to create an array that contains objects with an id and amount, grouped by id. The ids needs to be unique. So if there is 2 objects with same id, the amount will be added.
I can do it with nested for-loops, but I find this solution inelegant and huge. Is there a more efficient or cleaner way of doing it?
var bigArray = [];
// big Array has is the source, it has all the objects
// let's give it 4 sample objects
var object1 = {
id: 1,
amount: 50
}
var object2 = {
id: 2,
amount: 50
}
var object3 = {
id: 1,
amount: 150
}
var object4 = {
id: 2,
amount:100
}
bigArray.push(object1,object2,object3,object4);
// organizedArray is the array that has unique ids with added sum. this is what I'm trying to get
var organizedArray = [];
organizedArray.push(object1);
for(var i = 1; i < bigArray.length; i++ ) {
// a boolean to keep track whether the object was added
var added = false;
for (var j = 0; j < organizedArray.length; j++){
if (organizedArray[j].id === bigArray[i].id) {
organizedArray[j].amount += bigArray[i].amount;
added = true;
}
}
if (!added){
// it has object with new id, push it to the array
organizedArray.push(bigArray[i]);
}
}
console.log(organizedArray);
You can definitly make it cleaner and shorter by using reduce, not sure about efficiency though, i would say a traditional for loop is more efficient :
var bigArray = [];
var object1 = {id: 1, amount: 50}
var object2 = {id: 2, amount: 50}
var object3 = {id: 1, amount: 150}
var object4 = {id: 2, amount: 100}
bigArray.push(object1, object2, object3, object4);
var organizedArray = bigArray.reduce((acc, curr) => {
// check if the object is in the accumulator
const ndx = acc.findIndex(e => e.id === curr.id);
if(ndx > -1) // add the amount if it exists
acc[ndx].amount += curr.amount;
else // push the object to the array if doesn't
acc.push(curr);
return acc;
}, []);
console.log(organizedArray)
Rather than an organized array, how about a single object whose keys are the ids and values are the sums.
var bigArray = [
{ id: 1, amount: 50 },
{ id: 2, amount: 50 },
{ id: 1, amount: 150 },
{ id: 2, amount: 100 }
];
let total = {}
bigArray.forEach(obj => {
total[obj.id] = (total[obj.id] || 0) + obj.amount;
});
console.log(total);
If you really need to convert this to an array of objects then you can map the keys to objects of your choosing like this:
var bigArray = [
{ id: 1, amount: 50 },
{ id: 2, amount: 50 },
{ id: 1, amount: 150 },
{ id: 2, amount: 100 }
];
let total = {}
bigArray.forEach(obj => {
total[obj.id] = (total[obj.id] || 0) + obj.amount;
});
console.log(total);
// If you need the organized array:
let organizedArray = Object.keys(total).map(key => ({ id: key, amount: total[key] }));
console.log(organizedArray);
function getUniqueSums(array) {
const uniqueElements = [];
const arrayLength = array.length;
for(let index = 0; index < arrayLength; index++) {
const element = array[index];
const id = element.id;
const uniqueElement = findElementByPropertyValue(uniqueElements, 'id', id);
if (uniqueElement !== null) {
uniqueElement.amount += element.amount;
continue;
}
uniqueElements.push(element);
}
return uniqueElements;
}
function findElementByPropertyValue(array, property, expectedValue) {
const arrayLength = array.length;
for(let index = 0; index < arrayLength; index++) {
const element = array[index];
const value = element[property];
if (value !== expectedValue) {
continue;
}
return element;
}
return null;
}
This is an untested code. You will be able to understand the logic. Logic is almost same yours. But, perhaps a more readable code.

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

Categories