I need to get the next lower object in an array using a weight value.
const data = [
{ weight: 1, size: 2.5 },
{ weight: 2, size: 3.0 },
{ weight: 4, size: 3.5 },
{ weight: 10, size: 4.0 },
{ weight: 20, size: 5.0 },
{ weight: 30, size: 6.0 }
]
If the weight is 19, I need to get the object { weight: 10, size: 4.0 }.
With this attempt, I do get the closest object, but I always need to get the object with the next lowest value. If weight is smaller then 1, the first element should be returned.
const get_size = (data, to_find) =>
data.reduce(({weight}, {weight:w}) =>
Math.abs(to_find - w) < Math.abs(to_find - weight) ? {weight: w} : {weight}
)
If the objects' weights are in order, like in the question, one option is to iterate from the end of the array instead. The first object that matches will be what you want.
If the .find below doesn't return anything, alternate with || data[0] to get the first item in the array:
const data = [
{ weight: 1, size: 2.5 },
{ weight: 2, size: 3.0 },
{ weight: 4, size: 3.5 },
{ weight: 10, size: 4.0 },
{ weight: 20, size: 5.0 },
{ weight: 30, size: 6.0 }
]
const output = data.reverse().find(({ weight }) => weight < 19) || data[0];
console.log(output);
(if you don't want to mutate data, you can .slice it first)
Related
I have the following code:
const sample = [
{
name: "apple",
points: [ { x: 1, y: 2 }, { x: 3, y: 4 }, { x: 5, y: 6 }, { x: 7, y: 8 } ],
age: 24
},
{
name: "banana",
points: [ { x: 1, y: 2 }, { x: 3, y: 4 }, { x: 5, y: 6 }, { x: 7, y: 8 } ],
age: 45
}
];
const qwer = JSON.stringify(sample, null, 2);
console.log(qwer);
If you run it, you'll notice it has nice formatting, except for the points array, which is extremely verbose.
I would like everything to be indented like normally (which is why I'm passing in 2 for the final parameter to stringify), but I would like the points array to only take a single line, like how it is declared in the code.
The reason for this is because currently each points array is stretched to like 18 lines, when there will only ever be 3 or 4 items. I would like them to stay on one line.
I tried to use a custom replacer, and while it somewhat worked, it forced the JSON array to be a string. But it's not a string. I want it to stay an array.
Is there any way to do this?
For the general solution, a mini parser would be the best approach, but a quick but ugly-looking approach would be to use a replacer to replace arrays with stringified strings with a unique value as a prefix which can be replaced afterwards.
const sample = [
{
name: "apple",
age: 24,
points: [ { x: 1, y: 2 }, { x: 3, y: 4 }, { x: 5, y: 6 }, { x: 7, y: 8 } ],
},
{
name: "banana",
age: 45,
points: [ { x: 1, y: 2 }, { x: 3, y: 4 }, { x: 5, y: 6 }, { x: 7, y: 8 } ],
}
];
const withNestedStringifiedArrays = JSON.stringify(
sample,
(key, value) => key && Array.isArray(value) ? '##UNIQUE##' + JSON.stringify(value) : value,
2
);
const output = withNestedStringifiedArrays.replace(
/"##UNIQUE##(.*?)"(,)?$/gm,
(_, stringifiedArr, possibleComma = '') => stringifiedArr.replaceAll('\\', '') + possibleComma
);
console.log(output);
I have a MySQL table looking something like this as below:
id product_id option_type
-------------------------------
14 6 2
15 6 1
16 6 1
17 6 2
18 6 2
Applying the sql query with the help of Sequelize ORM on above sql table render the below array of objects and on option_type key I need to generate a combination of collection based on the option_type value:
var records = [
{ id: 14, value: "M", product_id: 6, option_type: 2 },
{ id: 15, value: "White", product_id: 6, option_type: 1 },
{ id: 16, value: "Black", product_id: 6, option_type: 1 },
{ id: 17, value: "S", product_id: 6, option_type: 2 },
{ id: 18, value: "L", product_id: 6, option_type: 2 }]
And I have managed with the Array.prototype.filter() method to create the combinations.
const option_type_1 = records.filter((el) => { return el.option_type == 1 });
const option_type_2 = records.filter((el) => { return el.option_type == 2 });
for(const opt1 of option_type_1) {
for(const opt2 of option_type_2) {
const options = {
"opt_one_id": opt1.id,
"opt_two_id": opt2.id,
};
optionList.push(options)
}
}
And getting the response as below...
{
"data": [
{ "opt_one_id": 15, "opt_one_value": White, "opt_two_id": 14, "opt_two_value": M },
{ "opt_one_id": 15, "opt_one_value": White, "opt_two_id": 17,"opt_two_value": S },
...and so on
]}
Now, How can I manage it as dynamically when option_type is added more.
Eg. now we are considering option_type = 1 and option_type = 2, but in future if one more option is added then option_type = 3 will be also there in the collection.
So, in that case how to manage this collection with the key name as well so at that time it will become something like this
{ "opt_one_id": 15, "opt_two_id": 14, "opt_three_id": 17 },
So any option_type is added or deleted it will be managed accordingly.
Thanks in advance !!!
=== Updated ===
Consider this base table of option_type
id option_type
-----------------
1 Colour
2 Size
So, accordingly it will generate the combinations as below
15 (White) * 14 (M)
15 (White) * 17 (S)
15 (White) * 18 (L)
16 (Black) * 14 (M)
16 (Black) * 17 (S)
16 (Black) * 18 (L)
You can group the results using option_type using underscore as follows.
const options = _.groupBy(records, record => record.option_type);
console.log(options);
once you grouped, you will get the following structure.(consider we have 3rd option type now)
options = {
1: [
{id: 15, product_id: 6, option_type: 1},
{id: 16, product_id: 6, option_type: 1}
],
2: [
{id: 14, product_id: 6, option_type: 2},
{id: 17, product_id: 6, option_type: 2 },
{id: 18, product_id: 6, option_type: 2 }
],
3: [
{id: 19, product_id: 6, option_type: 3},
]
}
after that, you can use for..in and for loop in combination to get the desired results as follows
const keys = Object.keys(options);
const rootItem = options[keys[0]];
delete options[keys[0]];
const finalResults = [];
for(let i =0; i< rootItem.length; i++) {
for (option in options) {
for (let j = 0; j<options[option].length; j++) {
let item = {};
item[`option_type_${rootItem[i].option_type}`] = rootItem[i].id;
item[`option_type_${options[option][j].option_type}`] = options[option][j].id;
finalResults.push(item);
}
}
}
console.log(finalResults)
[ { option_type_1: 15, option_type_2: 14 },
{ option_type_1: 15, option_type_2: 17 },
{ option_type_1: 15, option_type_2: 18 },
{ option_type_1: 15, option_type_3: 19 },
{ option_type_1: 16, option_type_2: 14 },
{ option_type_1: 16, option_type_2: 17 },
{ option_type_1: 16, option_type_2: 18 },
{ option_type_1: 16, option_type_3: 19 } ]
I need to filter this object array by minimum value of 'rest' attribute. This is an one way to do it. Is there any other ways ?
'data' variable is a result of chained function. Is there any other way to do this without calling 'data' variable again inside Math.min() function.
let data =
[ { size: 5, qty: 2, rest: 0 },
{ size: 2, qty: 5, rest: 0 },
{ size: 1, qty: 10, rest: 0 },
{ size: 3, qty: 3, rest: 1 },
{ size: 4, qty: 2, rest: 2 } ]
let result = data.filter(e=> e.rest === Math.min(...data.map(f=>f.rest) ) );
console.log(result);
// result is
//[ { size: 5, qty: 2, rest: 0 },
// { size: 2, qty: 5, rest: 0 },
// { size: 1, qty: 10, rest: 0 }]
The easiest way is to pull the min function out of the filter like this:
let min = Math.min(...data.map(item => item.rest))
This is much more efficient as we are no longer loop over the data to find the min for every iteration of the filter.
We now have n * 2 passes instead of n^2 passes. (n is the size of your data set, 5 in this case)
Full example below:
let data = [
{ size: 5, qty: 2, rest: 0 },
{ size: 2, qty: 5, rest: 0 },
{ size: 1, qty: 10, rest: 0 },
{ size: 3, qty: 3, rest: 1 },
{ size: 4, qty: 2, rest: 2 }
]
let min = Math.min(...data.map(item => item.rest))
let result = data.filter(item => item.rest === min)
console.log(result)
Hope this helps!
Lloyd
data.map inside of data.filter is O(N^2); for an O(N) solution, iterate through data ahead of time to calculate the minimum, then filter by that minimum:
let data =
[ { size: 5, qty: 2, rest: 0 },
{ size: 2, qty: 5, rest: 0 },
{ size: 1, qty: 10, rest: 0 },
{ size: 3, qty: 3, rest: 1 },
{ size: 4, qty: 2, rest: 2 } ];
const minRest = Math.min(...data.map(({ rest }) => rest));
let result = data.filter(({ rest }) => rest === minRest);
console.log(result);
imo. the simplest/best solution is the one #CertainPerformance gave you.
Just wanted to add another solution with linear runtime (that truly iterates only once over the Array)
let data = [
{ size: 5, qty: 2, rest: 0 },
{ size: 2, qty: 5, rest: 0 },
{ size: 1, qty: 10, rest: 0 },
{ size: 3, qty: 3, rest: 1 },
{ size: 4, qty: 2, rest: 2 }
];
let result = data.reduce((result, item) => {
let minRest = result.length? result[0].rest: item.rest;
if (item.rest < minRest) {
minRest = item.rest;
result.length = 0;
}
if (item.rest === minRest) {
result.push(item);
}
return result;
}, []);
console.log(result);
#mathieux51 got me another idea how you can do this inside a method chain, but the readability/clarity/intention is not as good as with the other approaches:
let data = [
{ size: 5, qty: 2, rest: 0 },
{ size: 2, qty: 5, rest: 0 },
{ size: 1, qty: 10, rest: 0 },
{ size: 3, qty: 3, rest: 1 },
{ size: 4, qty: 2, rest: 2 }
];
let result = data.sort((a, b) => a.rest - b.rest)
.filter((item, index, array) => item.rest === array[0].rest);
console.log(result);
It sounds like you want to sort the list. I would do it as following:
const result = data.sort((a, b) => a.rest - b.rest)
Get Min or Max
Since no one mentioned this method I will update it here.
myArray.sort(function (a, b) {
return a.rest - b.rest
})
var min = myArray[0],
max = myArray[myArray.length - 1]
It has good readability/clarity/intentions.
I want to add validation for following java script associative array.
Here is my array structure
array = [
{ id: 1, remaining_amount: 30, total: 20 },
{ id: 1, remaining_amount: 30, total: 20 },
{ id: 2, remaining_amount: 50, total: 40 }
]
From the above array, for id 1, remaining amount is 30 but total amount is 40
So i want to add the validation that if total amount is grater then remaining amount for particular id then it will show me message.
Any Idea?
You can use reduce and filter
Use reduceto summarise the total. and use filter to get all those array that total is greater than remaining_amount
var array = [{ id: 1, remaining_amount: 30, total: 20 },{ id: 1, remaining_amount: 30, total: 20 },{ id: 2, remaining_amount: 50, total: 40 }];
var result = Object.values(array.reduce((c, v) => {
c[v.id] = c[v.id] || {id: v.id,remaining_amount: v.remaining_amount,total: 0};
c[v.id].total += v.total;
return c;
}, {})).filter(o => o.remaining_amount < o.total);
console.log(result);
You can use this code -
array.forEach(function(obj) {
if (obj.total > obj.remaining_amount)
console.log(`for id ${obj.id}, remaining amount is ${obj.remaining_amount} but total amount is ${obj.total}`);
});
Trying to calculate shipping prices based on the weight of a package.
I have an Object containing prices for a given weight like
shippingPrices = {
'economy': {
[weight]: price,
. . .
}
};
Then I want to get the right shipping price with a function passing the weight of a package like:
addShippingPrice(700); // 700 is weight in grams
I've tried like so:
shippingPrices = {
'economy': {
2000: 7,
5000: 9,
10000: 10,
20000: 15,
30000: 22
}
};
var addShippingPrice = function(weight) {
var price = 0;
for( var maxWeight in shippingPrices.economy) {
maxWeight = parseInt(maxWeight, 10);
console.log(shippingPrices.economy[maxWeight]);
if(weight < maxWeight) {
// return price = shippingPrices.economy[maxWeight];
}
}
console.log('amount', price);
};
addShippingPrice(700);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
The "right" shipping price in this case would be 7 and if I call the function with weight 8000 addShippingPrice(8000); it should return 10
How can I achieve that? Also if it would be better to change the shippingPrices Object to Array I'll change it!
You could use an iterable structure, like
var shippingPrices = {
economy: [
{ weight: 2000, price: 7 },
{ weight: 5000, price: 9 },
{ weight: 10000, price: 10 },
{ weight: 20000, price: 15 },
{ weight: 30000, price: 22 }
],
priority: [
{ weight: 2000, price: 9 },
{ weight: 5000, price: 11 },
{ weight: 10000, price: 12 },
{ weight: 20000, price: 18 },
{ weight: 30000, price: 25 }
]
};
function getShippingPrice(type, weight) {
var price;
shippingPrices[type].some(function (a) {
if (a.weight >= weight) {
price = a.price;
return true;
}
});
return price;
}
var shippingPrices = { economy: [{ weight: 2000, price: 7 }, { weight: 5000, price: 9 }, { weight: 10000, price: 10 }, { weight: 20000, price: 15 }, { weight: 30000, price: 22 }], priority: [{ weight: 2000, price: 9 }, { weight: 5000, price: 11 }, { weight: 10000, price: 12 }, { weight: 20000, price: 18 }, { weight: 30000, price: 25 }] };
console.log(getShippingPrice('economy', 700));
console.log(getShippingPrice('economy', 8000));
ES6
function getShippingPrice(type, weight) {
return (shippingPrices[type].find(a => a.weight >= weight) || {}).price;
}
var shippingPrices = { economy: [{ weight: 2000, price: 7 }, { weight: 5000, price: 9 }, { weight: 10000, price: 10 }, { weight: 20000, price: 15 }, { weight: 30000, price: 22 }], priority: [{ weight: 2000, price: 9 }, { weight: 5000, price: 11 }, { weight: 10000, price: 12 }, { weight: 20000, price: 18 }, { weight: 30000, price: 25 }] };
console.log(getShippingPrice('economy', 700));
console.log(getShippingPrice('economy', 8000));
shippingPrices = {
'economy': {
2000: 7,
5000: 9,
10000: 10,
20000: 15,
30000: 22
},
'priority': {
2000: 9,
5000: 11,
10000: 12,
20000: 18,
30000: 25
}
};
var addShippingPrice = function(weight) {
var price = 0;
for (var maxWeight in shippingPrices.economy) {
maxWeight = parseInt(maxWeight);
if (weight <= maxWeight) {
price = shippingPrices.economy[maxWeight];
break;
}
}
return price;
};
var shipping_price = addShippingPrice(8000);
console.log('amount', shipping_price);
use below code, in case of maximum weight goes beyond the defined weight in object, you can set default Value for such case.
shippingPrices = {
'economy': {
2000: 7,
5000: 9,
10000: 10,
20000: 15,
30000: 22
}
};
var addShippingPrice = function (weight) {
$.each(shippingPrices.economy, function (key, value) {
if (weight <= key) {
console.log(value);
return false;
}
});
};
addShippingPrice(700); // 7
addShippingPrice(8000); // 10
You shouldn't rely on Object key sequence here, since as stated in the MDN doc
The for...in statement iterates over the enumerable properties of an object, in arbitrary order.
What you can do is to prepare an array of key weights and sort it. This array can be used then for comparison with the given value. Once you define the corresponding key weight, you'll ease get the price
shippingPrices = {
'economy': {
2000: 7,
5000: 9,
10000: 10,
20000: 15,
30000: 22
}
};
var getShippingPrice = function(weight) {
var weights = [];
for( var maxWeight in shippingPrices.economy) {
weights.push(parseInt(maxWeight, 10));
}
weights.sort(function(a,b){
return a - b;
});
for (var i = 0; i < weights.length; i++) {
if (weights[i] >= weight) {
return shippingPrices.economy[weights[i]];
}
}
return 0; // or return Math.max.apply(null, weights);
};
console.log('19000 => ', getShippingPrice(19000));
console.log('5000 => ', getShippingPrice(5000));
console.log('5001 => ', getShippingPrice(5001));
BTW, don't give name such "add..." to function which should just return a value