How to optimize array grouping? - javascript

I have an array that is constantly updated and accordingly it is necessary to update its grouping. Example of an array:
[
{
"price": 2419.62,
"amount": 0.0266
},
{
"price": 1927.52,
"amount": 0.0217
},
...
]
I tried different options. At the moment this option is the fastest:
const points = [
{
"price": 2419.62,
"amount": 0.0266
},
{
"price": 1927.52,
"amount": 0.0217
},
...
];
const range = 500;
const spread = 1800;
const countGroup = 250;
const sizeUnitGroup = range / countGroup;
const groups = {};
for (let i = 0; i < countGroup; i++){
groups[i] = [];
try {
points.forEach((item, id) => {
if (item.price > spread + (i*sizeUnitGroup) && item.price <= spread + (i*sizeUnitGroup + sizeUnitGroup)){
groups[i].push(item);
points.splice(id, 1);
}
if (item.price > (spread + (i*sizeUnitGroup + sizeUnitGroup))) throw BreakException;
});
} catch (e) {
}
}
But even so, this function works for too long. Any ideas how this can be optimized?

You could calculate the interval for pushing the value to the wanted slot.
var points = [
{ price: 2419.62, amount: 0.0266 },
{ price: 1927.52, amount: 0.0217 },
{ price: 1800, amount: 0.07 }, // -1 not in result
{ price: 1800.000000000001, amount: 0.07 }, // 0
{ price: 1802, amount: 0.07 }, // 0
],
range = 500,
spread = 1800,
countGroup = 250,
sizeUnitGroup = range / countGroup,
groups = {};
points.forEach((item, id) => {
var i = Math.ceil((item.price - spread- sizeUnitGroup) / sizeUnitGroup);
if (i >= 0 && i < countGroup) {
groups[i] = groups[i] || [];
groups[i].push(item);
}
});
console.log(groups);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Related

How to get max and min value in array of objects in javascript?

I need to get the maximum and minimum value of an array with several objects. I know that in Javascript we can use Math.min and Math.max for this, in addition to other strategies.
This solution of mine is working, but I'm finding the code too verbose. Can you tell me how to improve it?
Thank you very much in advance.
Here's my code I put into codesandbox.io
const skuStylePlu = [{
skuPrice: {
currentPrice: {
amount: 10
}
}
},
{
skuPrice: {
currentPrice: {
amount: 20
}
}
},
{
skuPrice: {
currentPrice: {
amount: 30
}
}
},
{
skuPrice: {
currentPrice: {
amount: 40
}
}
},
{
skuPrice: {
currentPrice: {
amount: 50
}
}
}
];
let lowest = Number.POSITIVE_INFINITY;
let highest = Number.NEGATIVE_INFINITY;
let temp;
for (let i = skuStylePlu.length - 1; i >= 0; i--) {
temp = skuStylePlu[i].skuPrice.currentPrice;
if (temp.amount < lowest) {
lowest = temp.amount;
}
if (temp.amount > highest) {
highest = temp.amount;
}
}
console.log(lowest, highest); // return 10, 50
use the reduce method instead of a for loop.
const skuStylePlu = [{
skuPrice: {
currentPrice: {
amount: 10
}
}
},
{
skuPrice: {
currentPrice: {
amount: 20
}
}
},
{
skuPrice: {
currentPrice: {
amount: 30
}
}
},
{
skuPrice: {
currentPrice: {
amount: 40
}
}
},
{
skuPrice: {
currentPrice: {
amount: 50
}
}
}
];
const prices = skuStylePlu.map(obj => obj.skuPrice.currentPrice.amount);
const lowest = prices.reduce((acc, curr) => Math.min(acc, curr), Number.POSITIVE_INFINITY);
const highest = prices.reduce((acc, curr) => Math.max(acc, curr), Number.NEGATIVE_INFINITY);
console.log(lowest, highest);
You could start with the value at index zero and get the value directly, bnot the last object.
Then loop until index one.
const
skuStylePlu = [{ skuPrice: { currentPrice: { amount: 10 } } }, { skuPrice: { currentPrice: { amount: 20 } } }, { skuPrice: { currentPrice: { amount: 30 } } }, { skuPrice: { currentPrice: { amount: 40 } } }, { skuPrice: { currentPrice: { amount: 50 } } } ];
let lowest = skuStylePlu[0].skuPrice.currentPrice.amount,
highest = skuStylePlu[0].skuPrice.currentPrice.amount,
i = skuStylePlu.length;
while (--i) {
const value = skuStylePlu[i].skuPrice.currentPrice.amount;
if (value < lowest) lowest = value;
if (value > highest) highest = value;
}
console.log(lowest, highest); // return 10, 50
You can use object destructuring to tidy things a little.
With a for of loop:
const skuStylePlu = [{"skuPrice":{"currentPrice":{"amount":10}}},{"skuPrice":{"currentPrice":{"amount":20}}},{"skuPrice":{"currentPrice":{"amount":30}}},{"skuPrice":{"currentPrice":{"amount":40}}},{"skuPrice":{"currentPrice":{"amount":50}}}];
let lowest = Number.POSITIVE_INFINITY;
let highest = Number.NEGATIVE_INFINITY;
for(const { skuPrice: { currentPrice: { amount } } } of skuStylePlu) {
lowest = lowest < amount ? lowest : amount;
highest = highest > amount ? highest : amount;
}
console.log(lowest, highest);
Single reduce for slightly increased terseness:
const skuStylePlu = [{"skuPrice":{"currentPrice":{"amount":10}}},{"skuPrice":{"currentPrice":{"amount":20}}},{"skuPrice":{"currentPrice":{"amount":30}}},{"skuPrice":{"currentPrice":{"amount":40}}},{"skuPrice":{"currentPrice":{"amount":50}}}];
const [lowest, highest] = skuStylePlu.reduce(
([low, high], { skuPrice: { currentPrice: { amount } } }) =>
[ low < amount ? low : amount, high > amount ? high : amount ],
[Number.POSITIVE_INFINITY, Number.NEGATIVE_INFINITY]
);
console.log(lowest, highest);

How to update value in array based on values in another array?

I am having two array like this,
let array1 = [
{
"id": 23,
"name": "Telangana",
}
]
Here i need to update array2 color value inside properties based on array1 numberOfProjects value inside latestMetric. As u can see that in both arrays stateId and id are same.If numberOfProjects value is in the range 1 - 1000. I need to update the color value as 1. then numberOfProjects value is in the range 1000 - 2000. I need to update the color value as 2.so on. I dont know how to achieve this. I tried to map those two arrays and can able to get the ID's.But i dont know how to compare them and update the value . Pleas help me.Thanks in advance
You can do like this
let updatedArr2 = [];
function updateArr2(arr2values, colorValue) {
let updatedProperties = { ...arr2values.properties, color: colorValue };
arr2values.properties = updatedProperties;
updatedArr2.push(arr2values);
}
array2.map(arr2values =>
array1.map(arr1values => {
if (arr2values.properties.stateId === arr1values.latestMetric.stateId) {
if (
arr1values.latestMetric.numberOfProjects >= 1 &&
arr1values.latestMetric.numberOfProjects <= 1000
) {
updateArr2(arr2values, 1);
} else if (
arr2values.latestMetric.numberOfProjects >= 1000 &&
arr2values.latestMetric.numberOfProjects <= 2000
) {
updateArr2(arr2values, 2);
}
}
})
);
console.log(updatedArr2);
You could loop through each object in array1 and then check if there's any object in array2 that matches the stateId, if so, then check the number of projects in the array1 object and change the color of the object in array2 that has the same stateId, something like:
array1.forEach((o) => {
let matches = array2.filter(
(o2) => o2.properties.stateId === o.latestMetric.stateId
);
let projects = o.latestMetric.numberOfProjects;
for (let match of matches) {
if (projects > 1 && projects < 1000) {
match.properties.color = 1;
} else if (projects >= 1000 && projects < 2000) {
match.properties.color = 2;
}
}
});
let array1 = [
{
id: 23,
name: "Telangana",
code: "lnn",
regionId: 1,
isActive: true,
latitude: 17.8495919,
longitude: 79.1151663,
latestMetric: {
stateId: 23,
year: 0,
constructionValueInMn: 84623,
constructionAreaInMnSqft: 32,
numberOfProjects: 406,
noOfCompletedProjects: 19,
noOfOngoingProjects: 387,
noOfUpcomingProjects: 0,
growthRate: 0,
averagePricePerSqftInRs: 0,
totalAreaInMnSqft: 71,
overAllAvgSqft: 0,
eachVariantAvgSqft: 0,
noOfTypeOfVariant: 0,
projectCompletionCycle: 0,
},
createdAt: "2020-04-21T00:35:11.684134",
updatedAt: "2020-04-21T00:35:11.684134",
},
];
let array2 = [
{
type: "Feature",
geometry: {
type: "Polygon",
coordinates: [
[
[77.19721, 28.861519],
[77.203836, 28.86004],
],
],
},
properties: {
cartodb_id: 26,
state_code: 7,
st_nm: "NCT of Delhi",
color: 2,
id: 23,
stateId: 23,
},
},
];
array1.forEach((o) => {
let matches = array2.filter(
(o2) => o2.properties.stateId === o.latestMetric.stateId
);
let projects = o.latestMetric.numberOfProjects;
for (let match of matches) {
if (projects > 1 && projects < 1000) {
match.properties.color = 1;
} else if (projects >= 1000 && projects < 2000) {
match.properties.color = 2;
}
}
});
console.log(array2);
Try this:
array2.map(arr2 => {
//Find to return the position when the id's are the same
const arr1 = array1.find(arr => arr.latestMetric.stateId == arr2.properties.id)
// If find was successful, do this
if (arr1) {
// Destructuring assignment to be easier to compare
const { numberOfProjects } = arr1.latestMetric
if (numberOfProjects >= 1 && numberOfProjects < 1000)
arr2.properties.color = 1
else if (numberOfProjects >= 1000 && numberOfProjects < 2000)
arr2.properties.color = 2
}
})

Javascript manipulating json issue

[
{
"timing": [
{
"zone": 18.8
},
{
"zone": 17.06,
},
{
"zone": 16.6
},
]
},
{
"timing": [
{
"zone": 12.6,
},
{
"zone": 14.6,
}
]
},
{
"timing": [
{
"zone":19.06,
},{
"zone": 8.06,
}
]
}
]
Here i am trying to work manipulate with one json data using javascript.
But, I am not able to think any approach how to achive that.
I am expecting below json. It give zone1, zone2, zone3 as per the zone any it will be dynamically
Please have a look to below json.
[
{
"zone1": [18.8, 12.6, 19.06 ]
},{
"zone2": [17.06, 14.6, 8.06]
}, {
"zone3":[16.6]
}
]
This is the output of json how it should look like.
Please have a look
You can use reduce and forEach
Loop through data, set OP's initial value as an object
Loop through timing property of each element, check if the zone + index + 1 exists in op or not, if exists push zone to that key else initialise new key
let data = [{"timing": [{"zone": 18.8},{"zone": 17.06,},{"zone": 16.6},]},{"timing": [{"zone": 12.6,},{"zone": 14.6,}]},{"timing": [{"zone": 19.06,}, {"zone": 8.06,}]}]
let final = data.reduce((op, { timing }) => {
timing.forEach(({ zone }, i) => {
let key = `zone${ 1 + i }`
op[key] = op[key] || []
op[key].push(zone)
})
return op
}, {})
console.log(final)
// If you need final output to be array of object just use entries and map to build a desired output
console.log(Object.entries(final).map(([k,v])=>({[k]:v})))
Here's a possible solution
var data = [{
"timing": [{
"zone": 18.8
},
{
"zone": 17.06,
},
{
"zone": 16.6
},
]
},
{
"timing": [{
"zone": 12.6,
},
{
"zone": 14.6,
}
]
},
{
"timing": [{
"zone": 19.06,
}, {
"zone": 8.06,
}]
}
];
// Calculate the total number of zones
var totalZones = 0;
for (let i = 0; i < data.length; i++) {
const currZones = data[i].timing.length;
if (currZones > totalZones) totalZones = currZones;
}
console.log(totalZones);
// Create the final Array
var result = new Array(totalZones);
for (let i = 0; i < totalZones; i++) {
result[i] = {
zone: []
}
}
// Populate the final array with values
for (let i = 0; i < totalZones; i++) {
for (let j = 0; j < data.length; j++) {
let currTiming = data[j].timing[i];
if (currTiming !== undefined) {
let currZone = data[j].timing[i].zone;
if (currZone !== undefined) {
result[i].zone.push(currZone);
}
}
}
}
console.log(result);
1) Gather all zone values into one array of array
2) Calculate max rows needed for zones
3) Have a simple for-loop till max rows and use shift and push methods.
const data = [
{
timing: [
{
zone: 18.8
},
{
zone: 17.06
},
{
zone: 16.6
}
]
},
{
timing: [
{
zone: 12.6
},
{
zone: 14.6
}
]
},
{
timing: [
{
zone: 19.06
},
{
zone: 8.06
}
]
}
];
const zones = data.map(time => time.timing.map(z => z.zone));
const rows = zones.reduce((rows, arr) => Math.max(rows, arr.length), 0);
const all = [];
for (let index = 1; index <= rows; index++) {
const res = [];
zones.forEach(zone => zone.length > 0 && res.push(zone.shift()));
all.push({ [`zone${index}`]: res });
}
console.log(all);
const input = [ {"timing": [{"zone": 18.8},{"zone": 17.06,},{"zone": 16.6},]},{"timing": [{"zone": 12.6,},{"zone": 14.6,}]},{"timing": [{"zone":19.06,},{"zone": 8.06,}]}]
var data = input.map(t => t.timing.map(u => u.zone));
var output = data[0].map((col, i) => data.map(row => row[i])).map((item, index) => {res = {}; res["zone"+(index+1)] = item.filter(t => t!==undefined); return res});
console.log(output);
Not the shortest, but it's very readable.
var json = [{"timing": [{"zone": 18.8},{"zone": 17.06,},{"zone": 16.6},]},{"timing": [{"zone": 12.6,},{"zone": 14.6,}]},{"timing": [{"zone": 19.06,}, {"zone": 8.06,}]}];
// index 0 is zone1, index 1 is zone2, index 2 is zone3, and so on ...
var zones = [];
// Loop through each 'timing' object
json.forEach(function(timingObject) {
var timing = timingObject['timing'];
// loop through each 'zone' in the given 'timing' object
timing.forEach(function(zoneObject, index) {
var zone = zoneObject['zone'];
// if the zone exists in the zones[] defined above
// add the current zone to its place
//
// if not (else), we have to add the array for the
// current index, then add the value of the current zone.
if(zones[index]) {
zones[index]['zone' + (index + 1)].push(zone);
} else {
zones.push({ ['zone' + (index + 1)]: [zone]})
}
});
});
console.log(zones);

I'm not sure what is happening with this project, it works locally but is having problem on server

I'm trying to move a project from local to server, and it's not working like it does in the local and I don't know what is going on. I have products and when I edit them it's not bringing the info from the database like it does locally, it brings 2 very important numbers as 0 when I know the database doesn't have those numbers as 0, without these the price cannot be calculated.
this is the information its bringing for a product
[ { "country": { "id": 1, "name": "COSTA RICA", "currency_symbol": "CRC" }, "country_id": 1, "margin": 0, "fi": 0 },
{ "country": { "id": 2, "name": "NICARAGUA", "currency_symbol": "COR" }, "country_id": 2, "margin": 0, "fi": 0 },
{ "country": { "id": 3, "name": "HONDURAS", "currency_symbol": "HNL" }, "country_id": 3, "margin": 0, "fi": 0 } ]
for this product in the database it has the margin and fi as
product_id: 1
country_id: 1
margin: 0.65
fi: 0.50
product_id: 1
country_id: 2
margin: 0.65
fi: 0.50
product_id: 1
country_id: 3
margin: 0.65
fi: 0.50
The edit products is a clone of the create model, for some reason it's not bringing the info like it should.
This is the function that opens the create and edit dialogs.
showDialog(model) {
if (this.processing) {
return;
}
if (model) {
let cloneModel = {...model};
this._formatModel(cloneModel);
this.actionText = 'Actualizar';
this.form = cloneModel
} else {
this.dialogTitle = 'Nuevo';
this.actionText = 'Agregar';
this._isNew();
if (this.form.id) {
this._resetForm();
this.$refs.form.resetFields()
}
}
this.dialogTitle = this._getDialogTitle(this.form);
this.dialogVisible = true
}
_formatModel(model) {
this.productCategories = [];
this.loadingResource = true;
this.$http.post(this.baseUrl + '/show/' + model.id).then(
(res) => {
this.model.export_factors = this.countries.map((country) => {
let result = res.body.export_factors.filter((item) => item.country_id === country.id);
let margin = 0;
let fi = 0;
if (result.length > 0) {
margin = result[0].margin;
fi = result[0].fi;
}
return {
country: country,
country_id: country.id,
margin: margin,
fi: fi,
}
});
this.model.prices = this.countries.map((country) => {
let result = res.body.prices.filter((item) => item.country_id === country.id);
let resultExport = res.body.export_factors.filter((item) => item.country_id === country.id);
let price = 0;
if (result.length > 0) {
price = (this.form.cif / resultExport[0].margin) / resultExport[0].fi;
}
return {
country: country,
country_id: country.id,
price: price.toFixed(2),
}
});
this.productCategories = res.body.categories.map((category) => {
category.fields = category.fields.map(item => {
item.allFields = [item.field];
return item;
});
return category;
});
this.form.tags = res.body.tags;
this.form.sizes = res.body.sizes;
this.loadingResource = false;
},
(res) => {
this.loadingResource = false;
this.dialogVisible = false;
this.$message.error(parseError(res)[0])
}
)
},
_isNew() {
this.model.prices = this.countries.map((country) => {
return {
country: country,
country_id: country.id,
price: 0,
}
});
this.model.export_factors = this.countries.map((country) => {
return {
country: country,
country_id: country.id,
fi: 0,
margin: 0
}
});
this.productCategories = [];
}
What could be happening?
There might be some problem with your comparison inside filter function. You can just parse the id into integer while comparing two ids as below. I'm just correcting your _formatModel
_formatModel(model) {
this.productCategories = [];
this.loadingResource = true;
this.$http.post(this.baseUrl + '/show/' + model.id).then(
(res) => {
this.model.export_factors = this.countries.map((country) => {
let result = res.body.export_factors.filter((item) => parseInt(item.country_id) === parseInt(country.id));
let margin = 0;
let fi = 0;
if (result.length > 0) {
margin = result[0].margin;
fi = result[0].fi;
}
return {
country: country,
country_id: country.id,
margin: margin,
fi: fi,
}
});
this.model.prices = this.countries.map((country) => {
let result = res.body.prices.filter((item) => parseInt(item.country_id) === parseInt(country.id));
let resultExport = res.body.export_factors.filter((item) => parseInt(item.country_id) === parseInt(country.id));
let price = 0;
if (result.length > 0) {
price = (this.form.cif / resultExport[0].margin) / resultExport[0].fi;
}
return {
country: country,
country_id: country.id,
price: price.toFixed(2),
}
});
this.productCategories = res.body.categories.map((category) => {
category.fields = category.fields.map(item => {
item.allFields = [item.field];
return item;
});
return category;
});
this.form.tags = res.body.tags;
this.form.sizes = res.body.sizes;
this.loadingResource = false;
},
(res) => {
this.loadingResource = false;
this.dialogVisible = false;
this.$message.error(parseError(res)[0])
}
)
},

javascript exclude some values in average calc

Here are some data:
data = [
{"Age":26,"Level":8},
{"Age":37,"Level":9},
{"Age":null,"Level":15},
{"Age":null,"Level":45}
];
from which I'm trying to calculate average for their properties:
var avg = {};
var rows = data.length;
data.forEach(obj => {
Object.keys(obj).forEach(k => {
if(obj[k] != null){
avg[k] = (avg[k] || 0) + obj[k] / rows;
}
});
});
return avg;
but the problem is in items that has properties with null values, where I'm trying to exclude null values from the calculation, and if you take a look at the the codepen there is Age: 15.75 instead of 31.5
because length of the data is always 4 (and should be 2 since 2 of them are null). How would be the best way to get the length to not be including the nulls?
You can have an object with nested object which has two properties value and count
const data = [
{"Age":26,"Level":8},
{"Age":37,"Level":9},
{"Age":null,"Level":15},
{"Age":null,"Level":45}
];
let avg = {}
data.forEach(x => {
for(let k in x){
if(!avg[k]){
avg[k] = {value:0,count:0};
}
if(x[k] !== null){
avg[k].value += x[k]
avg[k].count++;
}
}
})
avg = Object.fromEntries(Object.entries(avg).map(([k,v]) => ([k,v.value/v.count])))
console.log(avg)
let data = [
{"Age": 26, "Level": 8},
{"Age": 37, "Level": 9},
{"Age": null, "Level": 15},
{"Age": null, "Level": 45}
];
let averages = data.reduce((values, o) => {
Object.entries(o).forEach(([k, v]) => {
if (v !== null)
values[k] = (values[k] || []).concat(v);
});
return values;
}, {});
Object.entries(averages).forEach(([k, vs]) =>
averages[k] = vs.reduce((a, b) => a + b) / vs.length);
console.log(averages);
You can use a simple for...of and for...in loop to get the sum and count for each non-null item. You can add a get property to automatically calculate the average based on the sum and the count properties in the counter
const data = [{Age:26,Level:8},{Age:37,Level:9},{Age:null,Level:15},{Age:null,Level:45}];
let counter = {}
for (const item of data) {
for (const key in item) {
if (item[key] !== null) {
counter[key] = counter[key] || {
sum: 0,
count: 0,
get average() { return this.sum/this.count }
};
counter[key].sum += item[key]
counter[key].count++
}
}
}
console.log(counter)
I would do something like this: (not tested yet)
var data = [
{"Age":26,"Level":8},
{"Age":37,"Level":9},
{"Age":null,"Level":15},
{"Age":null,"Level":45}
];
var sum = { "Age": 0, "Level": 0 };
var average = { "Age": 0, "Level": 0 };
var sumCount = { "Age": 0, "Level": 0 };
// sum up all objects
for (var i = 0; i < data.length; i++) {
Object.keys(data[i]).forEach(function (key) {
if (data[i][key] == null || data[i][key] == undefined)
return;
sumCount[key]++;
sum[key] = sum[key] + data[i][key];
});
}
// make average object
Object.keys(average).forEach(function (key) {
average[key] = sum[key] / sumCount[key];
});
You could store the sum and count for every key independently.
var data = [{ "Age": 26, "Level": 8 }, { "Age": 37, "Level": 9 }, { "Age": null, "Level": 15 }, { "Age": null, "Level": 45 }],
avg = {},
temp = {};
data.forEach(obj => Object.keys(obj).forEach(k => {
if (obj[k] === null) return;
temp[k] = temp[k] || { sum: 0, count: 0 };
temp[k].sum += obj[k];
temp[k].count++;
avg[k] = temp[k].sum / temp[k].count;
}));
console.log(avg);
console.log(temp);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You could also do it in a relatively concise manner with just Array.reduce and inside of it just iterate over the Object.keys:
var data = [ {"Age":26,"Level":8}, {"Age":37,"Level":9}, {"Age":null,"Level":15}, {"Age":null,"Level":45} ];
let result = data.reduce((r, c) => (Object.keys(c).forEach(k => {
r[k] = (r[k] || { Sum: 0, Count: 0, Avg: 0 })
r[k].Sum += c[k] || 0
r[k].Count += c[k] ? 1 : 0
r[k].Avg = r[k].Sum / r[k].Count
}), r), {})
console.log(result)

Categories