Suppose I have the following array of objects:
myArray = [ { id: 'first', date: '2020-11-30', percentage: 10 }, { id: 'second', date: '2020-10-30', percentage: 20 }, { id: 'first', date: '2020-09-30', percentage: 30 } ]
Basically my question is how to find all the id's that have the same values, then compare their dates to see which has a higher value(I am planning on converting the string with Date.parse) and finally check which has the greater percentage, and then assign a variable to the condition.
Not really sure how to go about it, but figures it looks something like the code below or not, thanks for the help in advance.
for (i = 0; i < myArray.length; i++) {
if (myArray.id[i] === myArray.id[i]) {
if (myArray.date[i] > myArray.date[i]) {
if (myArray.percentage[i] > myArray.percentage[i]) {
let stuff = stuff;
}
}
}
}
You need to remember objects with the id that you've seen earlier so you can compare them with the object you're looking at "now" in each loop iteration. A Map is a good way to do that in modern JavaScript, or an object created with Object.create(null) in ES5.
const lastSeen = new Map();
for (const entry of myArray) {
const {id, date, percentage} = entry;
const last = lastSeen.get(id);
if (last) {
if (date > last.date && percentage > last.percentage) {
// ...this entry is newer than the previous one with the matching ID
// Replace the previous one (and possibly do something with `stuff`?)
lastSeen.set(id, entry);
}
} else {
lastSeen.set(id, entry);
}
}
Live Example:
const myArray = [ { id: 'first', date: '2020-11-30', percentage: 10 }, { id: 'second', date: '2020-10-30', percentage: 20 }, { id: 'first', date: '2020-09-30', percentage: 30 } ];
const lastSeen = new Map()
for (const entry of myArray) {
const {id, date, percentage} = entry;
const last = lastSeen.get(id);
if (last) {
console.log(`Checking ${id} / ${date} / ${percentage}...`);
if (date > last.date && percentage > last.percentage) {
// ...this entry is newer than the previous one with the matching ID
// Replace the previous one (and possibly do something with `stuff`?)
console.log(`Replacing ${id}...`);
lastSeen.set(id, entry);
} else {
console.log(`Not replacing ${id}`);
}
} else {
console.log(`${id} is new, adding...`);
lastSeen.set(id, entry);
}
}
I haven't included setting stuff above because it's not clear what let stuff = stuff; in your original code was meant to do. You can find the latest ones per id in lastSeen or do something where indicated above to handle stuff.
In ES5-level code (but here in 2020 about to be 2021, I strongly recommend writing modern code and using a transpiler if you need to support obsolete environments):
var lastSeen = Object.create(null);
for (let i = 0; i < myArray.length; ++i) {
var entry = myArray[i];
var last = lastSeen[entry.id];
if (last) {
if (entry.date > last.date && entry.percentage > last.percentage) {
// ...this entry is newer than the previous one with the matching ID
// Replace the previous one (and possibly do something with `stuff`?)
lastSeen[entry.id] = entry;
}
} else {
lastSeen[entry.id] = entry;
}
}
Live Example:
const myArray = [ { id: 'first', date: '2020-11-30', percentage: 10 }, { id: 'second', date: '2020-10-30', percentage: 20 }, { id: 'first', date: '2020-09-30', percentage: 30 } ];
var lastSeen = Object.create(null);
for (let i = 0; i < myArray.length; ++i) {
var entry = myArray[i];
var last = lastSeen[entry.id];
if (last) {
console.log(`Checking ${entry.id} / ${entry.date} / ${entry.percentage}...`);
if (entry.date > last.date && entry.percentage > last.percentage) {
// ...this entry is newer than the previous one with the matching ID
// Replace the previous one (and possibly do something with `stuff`?)
console.log(`Replacing ${entry.id}...`);
lastSeen[entry.id] = entry;
} else {
console.log(`Not replacing ${entry.id}`);
}
} else {
console.log(`${entry.id} is new, adding...`);
lastSeen[entry.id] = entry;
}
}
You could reduce the array with an object and check if the key exist or if the wanted properties are greater.
const
data = [{ id: 'first', date: '2020-11-30', percentage: 10 }, { id: 'second', date: '2020-10-30', percentage: 20 }, { id: 'first', date: '2020-09-30', percentage: 30 }],
result = Object.values(data.reduce((r, o) => {
if (
!r[o.id] ||
r[o.id].date < o.date ||
r[o.id].date === o.date && r[o.id].percentage < o.percentage
) {
r[o.id] = o;
}
return r;
}, {}));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Related
I have an array of objects, and each object has a nested object with a key that I would like to increment each iteration with a higher multiplier.
I have an object:
object = {
id: 1,
innerArr: [
{
key: 50
},
{
key: 20
}
]
}
then I want to:
Push it to a new array, X times.
multiply the 'key' with growing increments in each iteration
const arr = [];
let increment = 0.85;
for (let i = 0; i < 7; i++) {
const { id } = object;
object.innerArr.forEach(obj => {
obj.key *= increment;
})
arr.push({
id,
innerArr
})
increment += 0.05; // (1:) 0.9, (2:) 0.95...
}
The resulting array should look something like this:
arr = [
{
id: 1,
innerArr: [
{
key: 42.5
},
{
key: 17
}
]
},
{
id: 1,
innerArr: [
{
key: 45
},
{
key: 18
}
]
},
{
id: 1,
innerArr: [
{
key: 47.5
},
{
key: 19
}
]
} // and the rest...
]
But, for some (probably obvious) reason, when I do something similar, all the keys get the last increment only (the 7th iteration of increment += 0.05).
How can I get the desired output? and, what mechanism am I missing that cause this behavior.
Thanks for the help!
Found the answer! when I copied the innerArr, I did not actually copy its objects but referenced them, so the final arr contains only references to the initial object. So incrementing obj.key affects the initial innerArr object.
The solution is to:
deep copy the innerArr
make changes on the copy
push the copy to arr
start over
const arr = [];
let increment = 0.85;
for (let i = 0; i < 7; i++) {
const { id } = object;
let newInnerArr = JSON.parse(JSON.stringify(innerArr));
object.newInnerArr.forEach(obj => {
obj.key *= increment;
})
arr.push({
id,
newInnerArr
})
increment += 0.05; // (1:) 0.9, (2:) 0.95...
}
Hope it helps someone :)
I'm sorry if I'm asking a question that has been asked before, I had a hard time wording this one.
Basically, my API is returning an array of objects that are alike
const response.survey = [
{
1: { id: 1, user: user_1, points: 5 },
2: { id: 2, user: user_2, points: 3 }...
}
],
[
{
1: { id: 1, user: user_1, points: 10 },
2: { id: 2, user: user_2, points: 0 }...
}
],...
This can carry on for hundreds of arrays potentially, depending on users submissions through our forms. I'm stuck on how to combine them into something like:
[
{ user: user_1, points: 15 },
{ user: user_2, points: 3 },...
]
Note that the data that returned from the api has a key for the object, like Array[Object[1]], Array[Object[2]], etc.
In my current development environment, I'm returning 2x sets of 25 data points, so I would expect my final array of objects to be indexed at 25, but right now I'm getting 52. I believe there is a shorter way to handle this, but I cant quite figure it out and I keep going in circles.
Here is what I've tried:
let newArr = [];
response.survey.map((ballot, i) => {
for (const key in ballot) {
if (newArr.length == 0) {
let newObj = {
name: pos[key].user,
points: pos[key].points
}
newArr.push(newObj);
} else {
for (let k = 0; k < newArr.length; k++) {
if (newArr[k].name === pos[key].name) {
newArr[k].points += pos[key].points;
} else {
if (k + 1 == newArr.length) {
let newObj = {
name: pos[key].name,
points: pos[key].points
}
newArr.push(newObj);
}
}
}
}
}
}
I think I've been working on this issue for so long that I've started to go into circles and heading down the wrong path.
Any help would be greatly appreciated.
This solved my problem
await fetch('/api/getBallots')
.then((response) => response.json())
.then(async (data) => {
let newArray = []
data.ballots.map((ballot, i) => {
for (const key in ballot) {
if(!ballot[key].name) {
return;
} else {
let newObj = {
name: ballot[key].name,
points: ballot[key].points
}
newArray.push(newObj);
}
}
});
let rankings = [];
for (let i = 0; i < newArray.length; i++) {
if (rankings.length == 0) {
rankings.push({
name: newArray[i].name,
points: newArray[i].points
});
} else {
const index = rankings.findIndex((item) => item.name == newArray[i].name);
if (index == -1) {
rankings.push({
name: newArray[i].name,
points: newArray[i].points
})
} else {
rankings[index].points += newArray[i].points;
}
}
}
})
I want to get all the values that equal a certain number and count how many of each of the objects.
My code looks like this:
var countItems = {
"aa":"70",
"bb":"70",
"cc":"80",
"dd":"90",
"ee":"90",
"ff":"90"
}
Now what I want to do is count each on that is in the second half.
For example, there are two "70", one "80", and three 90. Then I can assign to variables:
var firstCounter = ?? // 2
var secondCounter = ?? // 1
var thirdCounter = ?? // 3
?? is I don't know what goes here.
If it was structed differently like the following, I could do it like this:
let firstCounter = 0;
for (let i = 0; i < countItems.length; i++) {
if (countItems[i].status === '70') firstCounter++;
}
let secondCounter = 0;
for (let i = 0; i < countItems.length; i++) {
if (countItems[i].status === '80') secondCounter++;
}
let thirdCounter = 0;
for (let i = 0; i < countItems.length; i++) {
if (countItems[i].status === '90') thirdCounter++;
}
But the thing is, my original code which is what I have is not structured like that, so I'm not sure how to adapt it.
How can I count the items in the original list (var countItems) so that I can find out how much each value is?
You could use Object.values(countItems) to get an array that looks like this: ["70","70","80","90","90","90"] then either use a for loop to conditionally increment whatever counters you want, or use something like Array.reduce or Array.filter to count the elements you need.
You could use reduce to create a counted hash map like so:
const countItems = [
{ data: 'aa', status: '70' },
{ data: 'bb', status: '70' },
{ data: 'cc', status: '80' },
{ data: 'dd', status: '90' },
{ data: 'ee', status: '90' },
{ data: 'ff', status: '90' },
];
const countedHash = countItems.reduce((acc, curr) => {
if (!acc[curr.status])
acc[curr.status] = 1
else
acc[curr.status] += 1
return acc
}, {})
/* print out the results */
console.log(countedHash)
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce
You can access object keys like this :
countItems["aa"] // it will return "70"
You can also loop on the object (if you want to do as you did in your example) :
for (const item in countItems) {
console.log(countItems[item])
if (countItems[item] == "70") firstCounter++;
}
Object.values() and reduce() are both the right ideas. Taken together...
var countItems = {
"aa":"70",
"bb":"70",
"cc":"80",
"dd":"90",
"ee":"90",
"ff":"90"
};
let counts = Object.values(countItems).reduce((acc, value) => {
if (!acc[value]) acc[value] = 0;
acc[value]++;
return acc;
}, {});
let [theFirstValue, theSecondValue, theThirdValue] = Object.values(counts)
console.log(theFirstValue, theSecondValue, theThirdValue);
const countItems = [
{ data: 'aa', status: '70' },
{ data: 'bb', status: '70' },
{ data: 'cc', status: '80' },
{ data: 'dd', status: '90' },
{ data: 'ee', status: '90' },
{ data: 'ff', status: '90' },
];
var countValues = Object.values(countItems);
let obj ={}
for(let val of countValues){
if(!obj[val.status]){
obj[val.status] = 1
}else{
obj[val.status] += 1
}
}
console.log(obj)
I have an Object as below:
const boxOfFruits = {
apples: [
{
name: "Kashmiri",
},
{
name: "Washington",
},
{
name: "Himalayan",
},
{
name: "Fuji",
}
],
oranges: [
{
name: "Nagpur",
},
{
name: "Clementine",
},
],
mangoes: [
{
name: "Totapuri",
},
{
name: "Alphonso",
},
{
name: "Langda",
},
],
}
I want to divide these fruits into boxes; maximum of n each, let's say where n is 3 and apples, oranges and mangoes are equally distributed.
So the output in this case would be:
box_1 = [{name: "Kashmiri"}, {name: "Nagpur"},{name: "Totapuri"}];
box_2 = [{name: "Washington"}, {name: "Clementine"},{name: "Alphonso"}];
box_3 = [{name: "Himalayan"},{name: "Langda"}, {name: "Fuji"}];
The type of fruits(apple,oranges,etc)/keys in object can increase/decrease and n is also variable. In case total fruits are less than n, then it would be just 1 box of fruits.
What I have tried so far:
Using Lodash, I am calculating the minimum and the maximum fruits in a single type:
const minFruitType = _.min(Object.values(basket).map((eachBasket: any) => eachBasket.length));
Total teams will the sum of the fruits / n
Will distribute the minimum fruits (l) in the first l boxes and fill the rest with the remaining fruits at every iteration while at the start of every iteration will calculate the minimum type of fruits again.
You can use Object.values(), array#reduce and array#forEach to transform your object.
const boxOfFruits = { apples: [ { name: "Kashmiri", }, { name: "Washington", }, { name: "Himalayan", }, ], oranges: [ { name: "Nagpur", }, { name: "Clementine", }, ], mangoes: [ { name: "Totapuri", }, { name: "Alphonso", }, { name: "Langda", }, ], },
result = Object.values(boxOfFruits).reduce((r, arr) => {
arr.forEach((o,i) => {
const key = `box_${i+1}`;
r[key] ??= r[key] || [];
r[key].push(o)
});
return r;
},{});
console.log(result);
The easiest way would be to use lodash.js's zip() function:
const boxes = _.zip( Object.values(boxOfFruits) );
Note that _.zip() will give you undefined values when the source arrays are different lengths, so you'll need/want to filter those out:
const boxes == _.zip( Object.values(boxOfFruits) )
.map(
box => box.filter(
x => x !== undefined
)
);
But that will not distribute the fruits evenly. For that, it shouldn't get much for difficult than this:
function distribute(boxOfFruits, n) {
const boxes = [];
const fruits = Object.keys(boxOfFruits);
for ( const fruit of fruits ) {
let i = 0;
const items = boxOfFruits[fruit];
for (const item of items) {
boxes[i] = !boxes[i] ?? [];
boxes[i] = boxes[i].push(item);
++i;
i = i < n ? i : 0 ;
}
}
return boxes;
}
A modified version of #Nicholas Carey's answer worked for me:
function distribute(boxOfFruits, n) {
let boxes = [];
let totalFruits = Object.values(boxOfFruits)
.reduce((content, current) => content + current.length, 0);
let maxBoxes = Math.ceil(totalFruits / 4);
Object.values(boxOfFruits).forEach((fruits) => {
let i = 0;
fruits.forEach((fruit) => {
boxes[i] ??= boxes[i] || [];
boxes[i].push(fruit);
++i;
i = i < (n+1) ? i : 0;
});
});
// Extra boxes created, redistribute them to
// starting boxes
let newBoxes = teams.slice(0, maxBoxes);
let pendingBoxes = teams.slice(maxBoxes);
let pendingFruits = pendingBoxes.flat();
let distributedBoxes = newBoxes.map((eachBox) => {
let required = n - eachBox.length;
if (required > 0) {
eachBox.push(...pendingFruits.splice(0, required));
}
return eachBox;
});
return distributedBoxes;
}
Code is pretty much the same as Nicholas's accept the below changes:
Directly fetched the values and iterated over those
empty array creation was failing, this way works
and checking on the max box size with n+1 instead of n
So I currently have a bunch of objects inside an array like below. However, I'm now trying to write a function that allows me to add another key|value into the object that was added last.
My current idea is using the arrayname.length - 1 to work out the position of the object within the array.
Would I need to create a temporary array to store the new object and then set (tempArray = oldArray) at the end of the function or would I concatinate them both?
const state = [
{
userId: 1,
},
{
Name: name,
},
{
age: 52,
},
{
title: "et porro tempora",
}]
this is the current code
let objects = [];
const addParent = (ev) =>{
ev.preventDefault();
// getting the length of the objects array
let arrayLength = objects.length;
// if the length of the array is zero - empty or one then set it to default zero
// else if there is objects stored in the array minus 1 to get the array position
if(arrayLength <= 0){
arrayLength = 0;
}else{
arrayLength = objects.length - 1;
}
//make a temporary array to be able to push new parent into an existing object
var tempObjects = []
for (var index=0; index<objects.length; index++){
}
//create a new parent object key : value
let parent = {
key: document.getElementById('key').value,
value: document.getElementById('value').value
}
//push parent object key and value into object
//objects.push(parent);
}
document.addEventListener('DOMContentLoaded', () => {
document.getElementById('btn1').addEventListener('click', addParent);
});
There are multiple ways to do this.
Try this one
var objects = [{
name: "1"
}];
const addParent = (ev) => {
let parent = {
key: "some value",
value: "some value"
}
objects = Array.isArray(objects) ? objects : [];
let lastObjectIndex = objects.length - 1;
lastObjectIndex = lastObjectIndex > -1 ? lastObjectIndex : 0
objects[lastObjectIndex] = { ...objects[lastObjectIndex],
...parent
}
}
I think You want to add new value to last object of the array
Method 1
const state = [
{
userId: 1,
},
{
Name: name,
},
{
age: 52,
},
{
title: "et porro tempora",
}]
state[state.length - 1].newKey = "value"
console.log(state)
Method 2
const state = [
{
userId: 1,
},
{
Name: name,
},
{
age: 52,
},
{
title: "et porro tempora",
}]
// 1st method
state[state.length - 1] = {
...state[state.length - 1] ,
newKey : "value"
}
console.log(state)
You can probably use something like this:
const a = [
{ "a" : "a" },
{ "b" : "b" }
]
const c = a.map((obj, index) => {
if (index === a.length -1) {
return { ...obj, newProp: "newProp" }
}
return obj;
});
console.log(c)
This will add property on the last object using spread operator, you can look it up if you are new to JS but basically it will retain all the existing property and add the newProp to the object