How to split an array of objects by arrays Javascript - javascript

I have an array of objects, i need to split this array to multiple arrays. If sum of element count <= 500, return these objects in array.
const array = [{idx: 1, count: 100}, {idx: 2, count: 200}, {idx: 3, count: 200}, {idx: 4, count: 100}]
//Expected Result: array of arrays
// [[{idx: 1, count: 100}, {idx: 2, count: 200}, {idx: 3, count: 200}], [{idx: 4, count: 100}]]

You can simply do it using reduce:
const array = [{idx: 1, count: 100}, {idx: 2, count: 200}, {idx: 3, count: 200}, {idx: 4, count: 100}]
const result = array.reduce((carry, item) => {
if (!carry.array.length || carry.count + item.count > 500) {
carry.array.push([item]);
carry.count = item.count;
} else {
carry.array[carry.array.length - 1].push(item);
carry.count += item.count;
}
return carry;
}, {array: [], count: 0}).array;
console.log(result);

This can be solved quite elegantly with generators:
function* groupTill(arr, predicate) {
let acc = [], pred = predicate();
for(const el of arr) {
if(!pred(el)) {
yield acc; acc = []; pred = predicate();
}
acc.push(el);
}
yield acc;
}
const result = [...groupTill(input, (total = 0) => ({ count }) => (total += count) < 500)];

You can use forEach to iterate through array and have two separate variables. One for the result array and another to hold the sum of count
const array = [{idx: 1, count: 100}, {idx: 2, count: 200}, {idx: 3, count: 200}, {idx: 4, count: 100}]
const res = [[]]; //initialize the result array with initial subarray
let count = 0; //initialize the count to zero
//Loop through the elements of array.
array.forEach(x => {
res[res.length - 1].push(x); //Add the the current element to the last sub array
count += x.count //increase the temporary count
//if count exceeds 500
if(count >= 500){
//add another sub array to the end of final array
res.push([]);
//Reset the count to 0
count = 0;
}
});
console.log(res);

You can use reduce with flags ( These are used to track whether we need to increase the index or not )
const array = [{idx: 1, count: 100}, {idx: 2, count: 200}, {idx: 3, count: 200}, {idx: 4, count: 100}]
let splitter = (arr) => {
let index = 0,
total = 0
return arr.reduce((op, inp) => {
if ((total + inp.count) > 500) {
index++;
total = 0;
}
total += inp.count
op[index] = op[index] || []
op[index].push(inp)
return op
}, [])
}
console.log(splitter(array))

Related

array reduce amount value into string format

I have an array of items, where i need to get a string of each product price.
const input = [{id: 1, amount: 20}, {id: 2, amount: 40}, {id: 3, amount: 90}]
const output = input?.reduce((acc, curr) => {
acc += `$${curr.amount}+`;
return acc;
}, '')
console.log(output)
Expected output is $20+$40+$90
But when i am trying this code i am getting the sum as $150 and i don't want to have + at the last if there are no more items.
Why Array.reduce()? This is a classic example for Array.map():
const input = [{id: 1, amount: 20}, {id: 2, amount: 40}, {id: 3, amount: 90}]
const expression = input.map(
({ amount }) => `$${amount}` // destructure the object, keep only .amount
).join('+');
console.log(expression);
Read about destructuring in the JavaScript documentation.
You can use map to extract the values followed by a join to create the string.
input.map(i => `$${i.amount}`).join('+')
Use split, and your code almost works
const input = [{id: 1, amount: 20}, {id: 2, amount: 40}, {id: 3, amount: 90}]
const output = input?.reduce((acc, curr) => {
acc.push(curr.amount + "$");
return acc;
}, []).join("+")
console.log(output)
const input = [{ id: 1, amount: 20 }, { id: 2, amount: 40 }, { id: 3, amount: 90 }]
const output = input?.slice(1).reduce((acc, curr) => {
acc += `+$${curr.amount}`;
return acc;
}, input.length ? `$${input[0].amount}` : '');
console.log(output)
with minimum manipulation
To add to the answers, we can use the currentIndex in the callback function in reduce as the third argument.
const input = [{id: 1, amount: 20}, {id: 2, amount: 40}, {id: 3, amount: 90}, {id: 4, amount: 55}]
const output = input?.reduce((acc, curr, index) => {
acc += `$${curr.amount}`;
if (index < input.length - 1) acc += '+'
return acc;
}, '')
console.log(output)
`

Building an array of sequential numbers

i have an incoming array:
[{step: 0, count: 1}, {step: 1, count: 5}, {step: 5, count: 5}]
so i need to transform incoming array in another array
[0, 1, 2, 3, 4, 5, 10, 15, 20, 25, 30]
I have tried to go this way:
const convertRangeData = (rangeData) =>
{
const convertedRangeData =
rangeData.reduce( (acc, item) =>
{
const { step, count } = item;
const prev = acc[acc.length - 1];
return [...acc, ...[...Array(count)].fill(step).map((i, idx) => i * (idx + 1) + prev)];
},[0] )
return convertedRangeData;
}
but I've got
[0, 0, 1, 2, 3, 4, 5, 10, 15, 20, 25, 30]
Use Array.from() to create an array with values in the ranges. Then iterate the array of ranges.
To create the continuous ranges reduce the array of ranges. When creating a range take the last number from the accumulator (acc), and use it as the start value.
const range = ({ step, count }, start = 0) =>
Array.from({ length: count }, (_, i) => (i + 1) * step + start)
const continuousRange = arr =>
arr.reduce((acc, r) => acc.concat(range(r, acc[acc.length -1])), [])
const ranges = [{step: 0, count: 1}, {step: 1, count: 5}, {step: 5, count: 5}]
const result = continuousRange(ranges)
console.log(result)
my way
const ranges = [{step: 0, count: 1}, {step: 1, count: 5}, {step: 5, count: 5}]
const convertRangeData = rangeData => rangeData.reduce((acc, {step,count}) =>
{
let prev = acc[acc.length - 1] || 0
while(count--)
acc.push(prev+=step)
return acc
},[])
console.log( convertRangeData(ranges) )

How to group items so as not to exceed a total amount in JavaScript?

I have an array of n objects in it. Each object has names as id, quantity, price and total. All of these have significance in solving this problem.
How can I create new arrays where sum of all total of their objects doesn't exceed 150.
My array:
let array = [{id: 1, quantity: 5, price: 10, total: 50}, {id: 2, quantity: 3, price: 100, total: 300}]
Expected result:
array1 = [{id: 1, quantity: 5, price: 10, total: 50}, {id: 2, quantity: 1, price: 100, total: 100}]
array2 = [{id: 2, quantity: 1, price: 100, total: 100}]
array3 = [{id: 2, quantity: 1, price: 100, total: 100}]
Conditions:
As mentioned, sum of totals in new arrays mustn't exceed 150
value of total must always be product of quantity and price in that object
object must keep dividing itself into new objects with smaller quantities until above conditions are met
quantity must be an integer
i tried this.
const itemsFinals = [];
const maxTotal = 150;
let totalGroup = 0;
for (i = 0; i < itemComprobantes.length; i++) {
if((itemComprobantes[i].total + totalGroup) < maxTotal){
itemsFinals.push(itemComprobantes[i]);
totalGroup += itemComprobantes[i].total;
}
}
let itemComprobantes = [
{id: 1, quantity: 5, price: 10, total: 50},
{id: 2, quantity: 1, price: 100, total: 100},
{id: 3, quantity: 1, price: 100, total: 100},
{id: 4, quantity: 1, price: 100, total: 30},
{id: 5, quantity: 1, price: 100, total: 80}
]
const itemsFinals = {}
const maxTotal = 150
let totalGroup = 0
let group = 1
for (i = 0; i < itemComprobantes.length; i++) {
if((itemComprobantes[i].total + totalGroup) > maxTotal){
group += 1
totalGroup = 0
}
if (itemsFinals['group '+group] === undefined) {
itemsFinals['group '+group] = []
}
itemsFinals['group '+group].push(itemComprobantes[i])
totalGroup += itemComprobantes[i].total
}
console.log(itemsFinals)
Here is my answer base on my comments.
Here is one way to do it. I know its long but give it a try
let array = [{id: 1, quantity: 5, price: 10, total: 50}, {id: 2, quantity: 3, price: 100, total: 300}, {id: 3, quantity: 5, price: 40, total: 200}, {id: 4, quantity: 2, price: 10, total: 20}];
const maxTotal = 150;
let new_arrays = {};
let index = 0;
let final_total = 0;
new_arrays['array_'+index] = [];
for (var obj of array) {
final_total += obj.total;
while (final_total > maxTotal) {
let exceed_value = final_total - maxTotal; // 350 - 150 = 200
let retain_quantity = parseInt((obj.total - exceed_value) / obj.price); // (300 - 200)/100 = 2
let remaining_quantity = obj.quantity - retain_quantity;
if (retain_quantity != 0) {
retain_obj = Object.assign({}, obj);
retain_obj.quantity = retain_quantity;
retain_obj.total = retain_quantity * obj.price;
new_arrays["array_"+index].push(retain_obj);
}
index++;
new_arrays['array_'+index] = [];
new_obj = Object.assign({}, obj, {quantity: remaining_quantity, total: remaining_quantity * obj.price})
new_arrays["array_"+index].push(new_obj);
// console.log(new_obj);
obj = new_obj;
final_total = remaining_quantity * obj.price;
new_arrays['array_'+index].pop(); // if loop is to repeat again
}
new_arrays["array_"+index].push(obj); // prints new and remaining quantities
}
console.log(new_arrays)

Javascript create new array from given array

I have an array of objects containing num and sum
[
{num: 1, sum: 10},
{num: 1, sum: 2},
{num: 1, sum: 3},
{num: 2, sum: 1},
{num: 2, sum: 40},
{num: 2, sum: 100},
{num: 3, sum: 0},
{num: 4, sum: 0},
]
How I want is:
[
{1: {count: 3, totalSum: 15},
{2: {count: 3, totalSum: 141},
{3: {count: 1, totalSum: 0},
{4: {count: 1, totalSum: 0},
]
Any help is highly appreciated
An option is to reduce into an object indexed by nums, and then map that object into an array:
const input=[{num:1,sum:10},{num:1,sum:2},{num:1,sum:3},{num:2,sum:1},{num:2,sum:40},{num:2,sum:100},{num:3,sum:0},{num:4,sum:0},]
const outputObj = input.reduce((a, { num, sum }) => {
if (!a[num]) a[num] = { count: 0, totalSum: 0 };
a[num].count++;
a[num].totalSum += sum;
return a;
}, {});
const output = Object.entries(outputObj)
.map(([key, val]) => ({ [key] : val }));
console.log(output);
You can use reduce method in order to group the array and then use another reduce method in order to find count and totalSum properties for every key.
let arr = [ {num: 1, sum: 10}, {num: 1, sum: 2}, {num: 1, sum: 3}, {num: 2, sum: 1}, {num: 2, sum: 40}, {num: 2, sum: 100}, {num: 3, sum: 0}, {num: 4, sum: 0}, ]
var groupBy = function(arr, key) {
return arr.reduce(function(acc, x) {
(acc[x[key]] = acc[x[key]] || []).push(x);
return acc;
}, {});
};
let grouped = groupBy(arr, 'num');
let result = Object.keys(grouped).reduce(function(acc, key){
let obj ={ [key] : {count : grouped[key].length, totalSum: grouped[key].map(a => a.sum).reduce((a,b) => a + b)}}
acc.push(obj)
return acc;
},[]);
console.log(result);

Sort other arrays by order of specific array?

I have a bunch of arrays in this form:
var myRows = [
[{idx: 0, val: 90}, {idx: 1, val: 75}, {idx: 2, val: 35}],
[{idx: 0, val: 50}, {idx: 1, val: 17}, {idx: 2, val: 95}],
[{idx: 0, val: 10}, {idx: 1, val: 24}, {idx: 2, val: 80}]
// ...
];
Lets say I would like to sort the first row ascending by val, so it becomes:
[{idx: 2, val: 35}, {idx: 1, val: 75}, {idx: 0, val: 90}]
Is there an easy way to sort the remaining arrays, so that their order matches the idx-order of the sorted first row?
myArrays = [
[{idx: 2, val: 35}, {idx: 1, val: 75}, {idx: 0, val: 90}]
, [{idx: 2, val: 95}, {idx: 1, val: 17}, {idx: 0, val: 50}]
, [{idx: 2, val: 80}, {idx: 1, val: 24}, {idx: 0, val: 10}]
// ...
];
Maybe this is even possible without the idx property?
You could use sorting with map and apply the mapping for all items.
This proposal saves the indices, order the array and applies the order to all other arrays as well.
// the array to be sorted
var list = [[{ idx: 0, val: 90 }, { idx: 1, val: 75 }, { idx: 2, val: 35 }], [{ idx: 0, val: 50 }, { idx: 1, val: 17 }, { idx: 2, val: 95 }], [{ idx: 0, val: 10 }, { idx: 1, val: 24 }, { idx: 2, val: 80 }]];
// temporary array holds objects with position and sort-value
var mapped = list[0].map(function (el, i) {
return { index: i, value: el.val };
})
// sorting the mapped array containing the reduced values
mapped.sort(function (a, b) {
return a.value - b.value;
});
// rearrange all items in list
list.forEach(function (a, i, aa) {
aa[i] = mapped.map(function (el) {
return a[el.index];
});
});
console.log(list);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You could do something like this.
var order = myRows[0].map(function(e) { return e.idx })
myRows.forEach(function(row) {
row.sort(function(a,b) {
return order.indexOf(a.idx) - order.indexOf(b.idx);
});
});
This is very simple code just to demonstate the idea.
It will probably be slow for very large arrays.
You can do the following which does,
Sort the first row of array1, and store their idxes in a temporary array2
Assign the remaining array with a temp property according to the first idx3
Sort the remaining array based on their temp property4 (Which is based on the first array)
Remove the temp property5
E.g.
var filteredRows = [];
var myRows = [
[{idx: 0, val: 90}, {idx: 1, val: 75}, {idx: 2, val: 35}],
[{idx: 0, val: 50}, {idx: 1, val: 17}, {idx: 2, val: 95}],
[{idx: 0, val: 10}, {idx: 1, val: 24}, {idx: 2, val: 80}]
];
/* 1. Sort the first row */
myRows[0].sort(function(a, b) {
return a.val - b.val;
});
filteredRows.push(myRows[0]);
/* 2. Get indexes */
var idxs = [];
for (var obj of myRows[0]) {
idxs.push(obj.idx);
}
/* Handle the remaining array */
myRows.slice(1).map(function (val) {
/* 3. Assign temp value */
val.map(function (obj, i) {
obj.temp = idxs[i];
});
/* 4. Sort them */
val.sort(function (a, b) {
return a.temp - b.temp;
});
/* 5. Remove temp value */
val.map(function (obj, i) {
delete obj.temp;
});
});
console.log(JSON.stringify(myRows));
When you drop the idx property, you can just use an array:
// Function copied from here: http://stackoverflow.com/a/36164530/5710637
var transpose = m => m[0].map((x,i) => m.map(x => x[i]))
var sortByRow = 0
var myRows = [
[90, 75, 35],
[50, 17, 95],
[10, 24, 80]
]
var myCols = transpose(myRows)
myCols.sort((x, y) => x[sortByRow] - y[sortByRow])
myRows = transpose(myCols)
console.log(myRows)
Use a hash table to create a sorting criteria based on the first row - see demo below:
var myRows=[[{idx:0,val:90},{idx:1,val:75},{idx:2,val:35}],[{idx:0,val:50},{idx:1,val:17},{idx:2,val:95}],[{idx:0,val:10},{idx:1,val:24},{idx:2,val:80}]];
// sort the first row (as desired)
myRows[0].sort((a,b) => a.val - b.val);
myRows.forEach(function(c,i){
if(i === 0){
// create order criteria based on first row
c.forEach(function(e, k){
this[e.idx] = k;
});
} else {
c.sort(function(a,b) {
return this[a.idx] - this[b.idx];
});
}
}, Object.create(null));
console.log(myRows);
.as-console-wrapper{top:0;max-height:100%!important;}

Categories

Resources