Related
I need my code to return the following:
{
status: 'OPEN',
change: [
['TWENTY', 60],
['TEN', 20],
['FIVE', 15],
['ONE', 1],
['QUARTER', 0.5],
['DIME', 0.2],
['PENNY', 0.04],
],
};
but right now it returns:
{
status: 'OPEN',
change: [
['TWENTY', 20],
['TWENTY', 20],
['TWENTY', 20],
['TEN', 10],
['TEN', 10],
['FIVE', 5],
['FIVE', 5],
['FIVE', 5],
['ONE', 1],
['QUARTER', 0.25],
['QUARTER', 0.25],
['DIME', 0.1],
['DIME', 0.1],
['PENNY', 0.01],
['PENNY', 0.01],
['PENNY', 0.01],
['PENNY', 0.01],
],
};
How can I sum all arrays whose first element is the same? I´ve tried using a loop, but it didn't work the way I wanted
let cashInDrawer = cid;
let cashInDrawerReverse = cashInDrawer.reverse();
let currencyUnits = [
["ONE HUNDRED", 100],
["TWENTY", 20],
["TEN", 10],
["FIVE", 5],
["ONE", 1],
["QUARTER", 0.25],
["DIME", 0.1],
["NICKEL", 0.05],
["PENNY", 0.01]
];
let availableMoney = [];
let totalAvailableMoney = 0;
let totalChange = cash - price;
let change = []
//Check the available money:
for(let index = 0; index < cid.length; index++){
availableMoney.push(cid[index][1]);
}
availableMoney.reverse();
//Total of available money:
for(let f = 0; f < availableMoney.length; f++){
totalAvailableMoney += availableMoney[f];
}
//Returns:
if(totalChange > totalAvailableMoney){
return {status: "INSUFFICIENT_FUNDS", change: []}
}
else if (totalChange === totalAvailableMoney){
return {status: "CLOSED", change: cashInDrawer.reverse()}
}
else {
for(var i = 0; i < cashInDrawerReverse.length; i++){
if(cashInDrawerReverse[i][1] != 0){
while(totalChange >= currencyUnits[i][1] && cashInDrawerReverse[i][1] > 0){
change.push(currencyUnits[i]);
cashInDrawerReverse[i][1] -= currencyUnits[i][1];
totalChange -= currencyUnits[i][1];
totalChange = totalChange.toFixed(2);
}
}
}
if(totalChange == 0){
return {status: "OPEN", change: change}
} else {
return {status: "INSUFFICIENT_FUNDS", change: []}
}
}
}
console.log(checkCashRegister(3.26, 100, [["PENNY", 1.01], ["NICKEL", 2.05], ["DIME", 3.1], ["QUARTER", 4.25], ["ONE", 90], ["FIVE", 55], ["TEN", 20], ["TWENTY", 60], ["ONE HUNDRED", 100]]))```
const change = [['TWENTY', 20],
['TWENTY', 20], ['TWENTY', 20], ['TEN', 10], ['TEN', 10], ['FIVE', 5], ['FIVE', 5], ['FIVE', 5], ['ONE', 1], ['QUARTER', 0.25], ['QUARTER', 0.25], ['DIME', 0.1], ['DIME', 0.1], ['PENNY', 0.01], ['PENNY', 0.01], ['PENNY', 0.01], ['PENNY', 0.01]]
const result = {}
for (const [denomination, amount] of change) {
if (!result[denomination]) {
result[denomination] = amount
} else {
result[denomination] += amount
}
}
console.log(Object.keys(result).map(key => ([key, result[key]])))
I would like to get the max of each type of bill using the reduce method.
The function has to itirate and compare each item to the next in line, then if the names match(item[0]) it should compare their values (item[1]) and store the biggest value with that original name.
I want to use reduce for this but am struggling to understanding how exactly accumulator is being applied here. Any suggestions? :)
const tempCollected= [
["TWENTY", 20],
["TWENTY", 40],
["TWENTY", 60],
["TEN", 10],
["TEN", 20],
["FIVE", 5],
["FIVE", 10],
["FIVE", 15],
["ONE", 1],
["QUARTER", 0.25],
["QUARTER", 0.5],
["DIME", 0.1],
["DIME", 0.2],
["PENNY", 0.01],
["PENNY", 0.02],
["PENNY", 0.03]
]
/* Desired outcome using reduce
[
["TWENTY", 60],
["TEN", 20],
["FIVE", 15],
["ONE", 1],
["QUARTER", 0.5],
["DIME", 0.2],
["PENNY", 0.03]
]
*/
/*My try */
const attempt=tempCollected.reduce(
(a,b,i,arr)=>{
if(b[i+1][0]===b[0]){ //if next item has the same name as the current
return [...a,b[0],Math.max(b[1],b[i+1][1])] //return Math.max(...of those two) + the original name of the bill
}
return [...a,b]
},[]
)
The way the problem appears to be set up requires a somewhat convoluted solution, since on each iteration, you may have to remove the previous element in the accumulator, or you may have to just add an element.
const tempCollected= [
["TWENTY", 20],
["TWENTY", 40],
["TWENTY", 60],
["TEN", 10],
["TEN", 20],
["FIVE", 5],
["FIVE", 10],
["FIVE", 15],
["ONE", 1],
["QUARTER", 0.25],
["QUARTER", 0.5],
["DIME", 0.1],
["DIME", 0.2],
["PENNY", 0.01],
["PENNY", 0.02],
["PENNY", 0.03]
];
const result = tempCollected.reduce((a, subarr) => {
// if no items have been iterated over yet, or if the type is new,
// push unconditionally
if (
!a.length || (
a[a.length - 1][0] !== subarr[0]
)) {
a.push(subarr);
return a;
}
// otherwise, remove the final item and push the new item
// if the final item's value is greater
if (subarr[1] > a[a.length - 1][1]) {
a.pop();
a.push(subarr);
}
return a;
}, []);
console.log(result);
If the method of solution isn't required to be reducing into an array, it would be much easier to group by turning it into an object instead.
const tempCollected= [
["TWENTY", 20],
["TWENTY", 40],
["TWENTY", 60],
["TEN", 10],
["TEN", 20],
["FIVE", 5],
["FIVE", 10],
["FIVE", 15],
["ONE", 1],
["QUARTER", 0.25],
["QUARTER", 0.5],
["DIME", 0.1],
["DIME", 0.2],
["PENNY", 0.01],
["PENNY", 0.02],
["PENNY", 0.03]
];
const grouped = {};
for (const [type, num] of tempCollected) {
grouped[type] = Math.max((grouped[type] || 0), num);
}
const result = Object.entries(grouped);
console.log(result);
I don't think reduce is the way to go here (it could obviously be done because at the end you are still looping through the array, but it seems more like an abuse of the method). Don't be afraid to do things more verbose! Short code does not mean better code.
Take a look at the following snippet:
const data = [
['TWENTY', 20],
['TWENTY', 40],
['TWENTY', 60],
['TEN', 10],
['TEN', 20],
['FIVE', 5],
['FIVE', 10],
['FIVE', 15],
['ONE', 1],
['QUARTER', 0.25],
['QUARTER', 0.5],
['DIME', 0.1],
['DIME', 0.2],
['PENNY', 0.01],
['PENNY', 0.02],
['PENNY', 0.03]
];
const group = (input) => {
const output = new Map();
input.forEach((item) => {
const [key, value] = item;
output.set(key, Math.max(output.get(key) || -Infinity, value));
});
return Array.from(output.entries());
};
console.log(group(data));
Just for completion, this would be the reduce method 'abuse'. Essentially it is used to store the output object or map as the accumulated value and loop at the same time:
const data = [
['TWENTY', 20],
['TWENTY', 40],
['TWENTY', 60],
['TEN', 10],
['TEN', 20],
['FIVE', 5],
['FIVE', 10],
['FIVE', 15],
['ONE', 1],
['QUARTER', 0.25],
['QUARTER', 0.5],
['DIME', 0.1],
['DIME', 0.2],
['PENNY', 0.01],
['PENNY', 0.02],
['PENNY', 0.03]
];
const result = data.reduce((acc, item, index, input) => {
acc[item[0]] = Math.max(acc[item[0]] || -Infinity, item[1]);
if (index < input.length - 1) {
return acc;
} else {
return Object.entries(acc);
}
}, {});
console.log(result);
I initially thought you can use something like:
const max = tempCollected.reduce((max, current) => current[1] > max[1] ? current : max)
Then I realized you wanted all of them. What you're trying to do isn't possible with reduce (or it's not the way to do it).
Doing array.reduce on the array will only return ["TWENTY", 20] as the item on the iteration, it won't give you the entire group of items that you want to accumulate. You either have to manually do it or you have to restructure your array.
const items = {
twenty: 0,
ten: 0,
five: 0,
one: 0,
...
}
collected.forEach((item) => {
if (item[1] > items[item[0]]) {
items[item[0]] = item[1]
}
}
Or something like that depending on how you structure your data.
Can be a oneliner using Array.reduce to create an object and convert its result back to an Array with Object.entries. In the snippet the initial array is shuffled (unsorted), so an extra sort is added to sort the result descending on the result values. Note that using this reducer lambda the order of initial values has become irrelevant.
// shuffled the values a bit
const temp = [
["TWENTY", 60],
["FIVE", 5],
["PENNY", 0.03],
["TWENTY", 40],
["TEN", 10],
["QUARTER", 0.5],
["TEN", 20],
["FIVE", 10],
["ONE", 1],
["FIVE", 15],
["TWENTY", 20],
["QUARTER", 0.25],
["DIME", 0.1],
["PENNY", 0.01],
["DIME", 0.2],
["PENNY", 0.02], ];
const collectedMaxValues = Object.entries(
temp.reduce( (acc, [key, val]) =>
( {...acc, [key]: (acc[key] || 0) > val ? acc[key] : val } ), {} )
).sort( ([,val1], [,val2]) => val2 - val1);
console.log(JSON.stringify(collectedMaxValues));
Maybe not the most elegant way, but this method doesn't rely on external variables to store end result, it converts reducer (a) into nested array on first step and with each iteration checks if the reducer already contains same name, if it does, updates the maximum value, otherwise adds a new item:
const tempCollected = [
["TWENTY", 20],
["TWENTY", 40],
["TWENTY", 60],
["TEN", 10],
["TEN", 20],
["FIVE", 5],
["FIVE", 10],
["FIVE", 15],
["ONE", 1],
["QUARTER", 0.25],
["QUARTER", 0.5],
["DIME", 0.1],
["DIME", 0.2],
["PENNY", 0.01],
["PENNY", 0.02],
["PENNY", 0.03]
]
/* Desired outcome using reduce
[
["TWENTY", 60],
["TEN", 20],
["FIVE", 15],
["ONE", 1],
["QUARTER", 0.5],
["DIME", 0.2],
["PENNY", 0.03]
]
*/
const attempt = tempCollected.reduce((a, b, i) =>
{
if (i == 1)
a = [a];
for (i = 0; i < a.length; i++)
{
if (a[i][0] == b[0])
return a[i][1] = Math.max(a[i][1], b[1]), a;
}
return a.push(b), a;
});
console.log("attempt", attempt)
These all are the tags which I'm storing into in an array with those who have Type_ as a prefix
My array have multiple type of values but I'm unable to sort them here is the array
{
Type_5000-10000mAh: [10, "5000-10000mAh", "type_5000-10000mah", "Type_5000-10000mAh", 70],
Type_15000-20000mAh: [1, "15000-20000mAh", "type_15000-20000mah", "Type_15000-20000mAh", 76],
Type_USB-C: [4, "20000-30000mAh", "type_20000-30000mah", "Type_20000-30000mAh", 95],
Type_20000-30000mAh: [4, "20000-30000mAh", "type_20000-30000mah", "Type_20000-30000mAh", 95],
Type_Wireless: [3, "Wireless", "type_wireless", "Type_Wireless", 160],
Type_Quick Charger: [1, "Quick Charger", "type_quick-charger", "Type_Quick Charger", 344]
}
and the output I want is like that
{
Type_5000-10000mAh: [10, "5000-10000mAh", "type_5000-10000mah", "Type_5000-10000mAh", 70],
Type_15000-20000mAh: [1, "15000-20000mAh", "type_15000-20000mah", "Type_15000-20000mAh", 76],
Type_20000-30000mAh: [4, "20000-30000mAh", "type_20000-30000mah", "Type_20000-30000mAh", 95],
Type_USB-C: [4, "20000-30000mAh", "type_20000-30000mah", "Type_20000-30000mAh", 95],
Type_Wireless: [3, "Wireless", "type_wireless", "Type_Wireless", 160],
Type_Quick Charger: [1, "Quick Charger", "type_quick-charger", "Type_Quick Charger", 344]
}
and this is the code I'm using to get this array format
var tags_type = {};
var indexI = 0;
$.each(totPro.products, (productsKey, productsValue) => {
$.each(productsValue.tags, (prodKey, prodVal) => {
indexI++;
if(prodVal.toLowerCase().indexOf('type') > -1){
console.log(prodVal);
if(tags_type[prodVal] == undefined){
tags_type[prodVal] = [0, capitalizeFirstLetter(prodVal.slice(5)), prodVal.toLowerCase().replace(/\s+/g, '-').toLowerCase(), prodVal, indexI];
}
tags_type[prodVal][0]++;
}
});
});
console.log(tags_type);
You do NOT have an array.
you have an object where you cannot determine the sort order.
You CAN sort the keys and then display the object in that order using localeCompare
Here is a more elaborate explanation: Sort JavaScript object by key
const obj = {
"Type_5000-10000mAh": [10, "5000-10000mAh", "type_5000-10000mah", "Type_5000-10000mAh", 70],
"Type_15000-20000mAh": [1, "15000-20000mAh", "type_15000-20000mah", "Type_15000-20000mAh", 76],
"Type_USB-C": [4, "20000-30000mAh", "type_20000-30000mah", "Type_20000-30000mAh", 95],
"Type_20000-30000mAh": [4, "20000-30000mAh", "type_20000-30000mah", "Type_20000-30000mAh", 95],
"Type_Wireless": [3, "Wireless", "type_wireless", "Type_Wireless", 160],
"Type_Quick Charger": [1, "Quick Charger", "type_quick-charger", "Type_Quick Charger", 344]
}
const sortAlphaNum = (a, b) => a.localeCompare(b, 'en', { numeric: true });
const showObj = obj => Object.keys(obj).sort(sortAlphaNum).forEach(key => console.log(key,obj[key]));
showObj(obj)
If you really want to have a sorted object, then here is a rewrite from the link I posted
const obj = {
"Type_5000-10000mAh": [10, "5000-10000mAh", "type_5000-10000mah", "Type_5000-10000mAh", 70],
"Type_15000-20000mAh": [1, "15000-20000mAh", "type_15000-20000mah", "Type_15000-20000mAh", 76],
"Type_USB-C": [4, "20000-30000mAh", "type_20000-30000mah", "Type_20000-30000mAh", 95],
"Type_20000-30000mAh": [4, "20000-30000mAh", "type_20000-30000mah", "Type_20000-30000mAh", 95],
"Type_Wireless": [3, "Wireless", "type_wireless", "Type_Wireless", 160],
"Type_Quick Charger": [1, "Quick Charger", "type_quick-charger", "Type_Quick Charger", 344]
}
const sortAlphaNum = (a, b) => a.localeCompare(b, 'en', { numeric: true });
const sortedObj = Object.keys(obj)
.sort(sortAlphaNum)
.reduce((acc, key) => { acc[key] = obj[key]; return acc; },{});
console.log(sortedObj)
Mplungian is right in saying that the order of keys in objects is not specified to be preserved in JavaScript. But having said this, many JS implementations do return the keys in the order they were generated. So, maybe you get lucky with the following:
const o={ "Type_5000-10000mAh": [10, "5000-10000mAh", "type_5000-10000mah", "Type_5000-10000mAh", 70], "Type_15000-20000mAh": [1, "15000-20000mAh", "type_15000-20000mah", "Type_15000-20000mAh", 76], "Type_USB-C": [4, "20000-30000mAh", "type_20000-30000mah", "Type_20000-30000mAh", 95], "Type_20000-30000mAh": [4, "20000-30000mAh", "type_20000-30000mah", "Type_20000-30000mAh", 95], "Type_Wireless": [3, "Wireless", "type_wireless", "Type_Wireless", 160], "Type_Quick Charger": [1, "Quick Charger", "type_quick-charger", "Type_Quick Charger", 344] };
const pr=s=>s.replace(/\_(\d+)/,(_,m)=>"_"+m.padStart(5,"0"));
p=Object.fromEntries(
Object.entries(o).sort(([a],[b])=>
pr(a).localeCompare(pr(b))) );
console.log(p)
p is a newly created object where the keys have been added in the desired order.
#mplungians comparison version (with {numeric:true}) will of course work here just as well!
I am creating a dashboard to show data from various stats. I am using Django and Google Charts for creating graphs. It has been good so far but I am stuck at one particular case.
the model class is-
class Registration(models.Model):
event_type = models.CharField(max_length=80)
date_time = models.DateField()
count = models.IntegerField()
my query is-
Registration.objects.filter(event_type__in=['VHRAssmntCompleted',
'VNAAssmntCompleted',
'NonsmokersDeclrtn',
'MWBAssmntCompleted',
'VHCAssmntCompleted',
'SV Document Uploads',
'PapSmear',
'Mammogram',],
date_time__range=(d3,d1)).order_by('date_time')
I get the data in following format:
[["VHR", "2019-02-1", 23],
["VNA", "2019-02-1", 34],
["PAP", "2019-02-1", 50],
["VHR", "2019-02-2", 92],
["VNA", "2019-02-2", 13],
["PAP", "2019-02-2", 65],
["VHR", "2019-02-3", 192],
["VNA", "2019-02-3", 43],
["PAP", "2019-02-3", 11]]
To create a Combo Chart in need the data in following format(something like python dataframe):
[["date", "VHR", "VNA", "PAP" ],
["2019-02-1", 23,34,50],
["2019-02-2", 92,13,65],
["2019-02-3", 192,43,11]]
I am unable to find a way to do this, either format it using Django ORM query itself or transform using JS.
I need help with what approach should I go.
You could take an object for the column position of the values and collect the data by date and column.
var data = [["VHR", "2019-02-1", 23], ["VNA", "2019-02-1", 34], ["PAP", "2019-02-1", 50], ["VHR", "2019-02-2", 92], ["VNA", "2019-02-2", 13], ["PAP", "2019-02-2", 65], ["VHR", "2019-02-3", 192], ["VNA", "2019-02-3", 43], ["PAP", "2019-02-3", 11]],
cols = { VHR: 1, VNA: 2, PAP: 3 },
result = data.reduce((r, [key, date, value]) => {
var row = r.find(([d]) => d === date);
if (!row) {
r.push(row = [date, 0, 0, 0]);
}
row[cols[key]] = value;
return r;
}, [["date", "VHR", "VNA", "PAP"]]);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
you can group them by date, then using Object.entries loop through the grouped result and transform it to [date, "VHR", "VNA", "PAP"]
const data = [
["VHR", "2019-02-1", 23],
["VNA", "2019-02-1", 34],
["PAP", "2019-02-1", 50],
["VHR", "2019-02-2", 92],
["VNA", "2019-02-2", 13],
["PAP", "2019-02-2", 65],
["VHR", "2019-02-3", 192],
["VNA", "2019-02-3", 43],
["PAP", "2019-02-3", 11]
];
const grouped = data.reduce((all, [key, date, value]) => {
all[date] = {
...all[date],
[key]: value
};
return all;
}, {});
const result = ["date", "VHR", "VNA", "PAP"].concat(
Object.entries(grouped).map(([date, obj]) => [date, ...Object.values(obj)])
);
console.log(result);
I have two arrays of array in Javascript like
var array1 = [[10, 2], [11, 4], [12, 30], [13, 17], [14, 28]];
var array2 = [[8, 13], [9, 19], [10, 6], [11, 7], [12, 1]];
I want to get the set of arrays from array1 which match the first element of each array of the array2
in my example case both array1 and array2 have array with first element as 10 11 and 12, so it should return
[[10, 2], [11, 4], [12, 30]];
is there any easy and efficient way using pure javscript or lodash, underscor framework or something like that. Without iterate over and match one by one of this two array ?
In ES6, you could use Set.
var array1 = [[10, 2], [11, 4], [12, 30], [13, 17], [14, 28]],
array2 = [[8, 13], [9, 19], [10, 6], [11, 7], [12, 1]],
set = new Set(array2.map(a => a[0])),
result = array1.filter(a => set.has(a[0]));
console.log(result);
Version with an object as hash table
var array1 = [[10, 2], [11, 4], [12, 30], [13, 17], [14, 28]],
array2 = [[8, 13], [9, 19], [10, 6], [11, 7], [12, 1]],
result = array1.filter(function (a) {
return this[a[0]];
}, array2.reduce(function (r, a) {
r[a[0]] = true;
return r;
}, Object.create(null)));
console.log(result);
You can use lodash _.intersectWith function in order to solve this problem in an inline.
_.intersectionWith(array1, array2, function(a, b) {
return a[0] === b[0];
});
I don't know about performance cos I haven't had the chance to have a look at the source code of this function. Anyway, I like it for its simplicity. Here's the fiddle in case you want to check it out.
If you can make use of Set, then you can compute the a set of numbers to look for first and use .filter to only get the arrays whose first element is in that set:
var haystack = new Set(array2.map(x => x[0]));
var newArray = array1.filter(x => haystack.has(x[0]));
Of course you can also use the lodash or underscore versions of .map and .filter.
Alternatives to using Set would be:
Create an array of numbers instead and use indexOf to test existence. That will scale linearly with the number of elements:
var haystack = array2.map(x => x[0]);
var newArray = array1.filter(x => haystack.indexOf(x[0]) > -1);
Create an object with number -> true entries to test existence with in, hasOwnProperty or just object access:
var haystack = array2.reduce((obj, x) => (obj[x[0]] = true, obj), {});
var newArray = array1.filter(x => haystack[x[0]]);
Which one performs better depends on the number of elements you have and the environment the code is running in.
You can do this with filter() and find()
var array1 = [
[10, 2],
[11, 4],
[12, 30],
[13, 17],
[14, 28]
];
var array2 = [
[8, 13],
[9, 19],
[10, 6],
[11, 7],
[12, 1]
];
var result = array1.filter(function(ar) {
return array2.find(function(e) {
return e[0] == ar[0]
})
})
console.log(result)
I would do this with a Map anf filter combo in ES6 as follows;
var array1 = [[10, 2], [11, 4], [12, 30], [13, 17], [14, 28]],
array2 = [[8, 13], [9, 19], [10, 6], [11, 7], [12, 1]],
m = new Map(array2),
array3 = array1.filter(a => m.has(a[0]));
console.log(array3);
If you need backwards compatibility other answers are pretty good.