Replace value in table based on another table - javascript

Is there a best practice in using arquero to reformat a value based on a set of ranges?
For example, I have the following 2 arrays:
const agefmt = [
{
'fmtname': 'agefmt',
'type' : 'n',
'format': [
{'start': 0, 'end': 10, 'label': '0 - 10'},
{'start': 11, 'end': 20, 'label': '11 - 20'},
{'start': 21, 'end': 30, 'label': '21 - 30'},
{'start': 31, 'end': 40, 'label': '31 - 40'},
{'start': 41, 'end': 50, 'label': '41 - 50'},
{'start': 51, 'end': 1000, 'label': '>51'}
]
},
]
const age = [
{ "AGE": 19 },
{ "AGE": 20 },
{ "AGE": 31 },
{ "AGE": 26 },
{ "AGE": 46 },
{ "AGE": 27 }
]
and I would like to replace the 'age' array with the 'label' within the range of the 'start' and 'end' values defined in the agefmt array.
The resulting array should look like the following:
[
{ "AGE": '0 - 10' },
{ "AGE": '11 - 20' },
{ "AGE": '31 - 40' },
{ "AGE": '21 - 30' },
{ "AGE": '41 - 60' },
{ "AGE": '21 - 30' }
]
I started writing it as a function like so, but found I couldn't pass the 'agefmt' as a parameter:
addFunction(function fmt({infmt, value}) {
console.log(value, infmt)
// calculate ranges here
return value
});
r.derive({rowvar : d => op.fmt(rowgrp, d.rowvar)})
Is there a more obvious way to achieve this?

It is just a matter of iterating through the age array, and then checking the nested AGE property against agefmt[0].format in a for loop. Whenever the AGE value is between the start and end values, then a label is considered found and we break out of the for loop.
One thing is that your expected output is incorrect based on your provided data: the first entry has an age of 19 and the matching label should be 11 - 20 not 0 - 10 as you wanted.
See proof-of-concept below:
const agefmt = [
{
fmtname: "agefmt",
type: "n",
format: [
{ start: 0, end: 10, label: "0 - 10" },
{ start: 11, end: 20, label: "11 - 20" },
{ start: 21, end: 30, label: "21 - 30" },
{ start: 31, end: 40, label: "31 - 40" },
{ start: 41, end: 50, label: "41 - 50" },
{ start: 51, end: 1000, label: ">51" },
],
},
];
const age = [
{ AGE: 19 },
{ AGE: 20 },
{ AGE: 31 },
{ AGE: 26 },
{ AGE: 46 },
{ AGE: 27 },
];
function addLabelToAge(age, labelFormat) {
return age.map(entry => {
let matchedLabel = '';
for (const format of labelFormat) {
if (entry.AGE >= format.start && entry.AGE <= format.end) {
matchedLabel = format.label;
break;
}
}
return { AGE: matchedLabel };
});
}
console.log(addLabelToAge(age, agefmt[0].format));
If you're comfortable with ES6 syntax, you can make the function a little more concise using object destructuring + object property assignment shorthand:
function addLabelToAge(age, labelFormat) {
return age.map(({ AGE }) => {
let matchedLabel = '';
for (const { start, end, label } of labelFormat) {
if (AGE >= start && AGE <= end) {
matchedLabel = label;
break;
}
}
return { AGE: matchedLabel };
});
}

Related

How merge two arrays conditional on inner keys?

I have two arrays which look like below:
array1 = [
{
id: 'A',
values: [
{ date: '1/1/2022', measure: 231 },
{ date: '1/2/2022', measure: 31 },
],
},
{
id: 'B',
values: [
{ date: '1/1/2020', measure: 51 },
{ date: '1/2/2020', measure: 66 },
],
},
];
const array2 = [
{
id: 'AA',
values: [
{ date: '1/1/2022', measure: 23 },
{ date: '1/2/2022', measure: 67 },
],
},
{
id: 'BB',
values: [
{ date: '1/1/2020', measure: 90 },
{ date: '1/2/2020', measure: 100 },
],
},
];
The arrays have unequal ids but it is known key A should be merged with AA, B should be merged with BB and so on. Dates are equal in case of A and AA, B and BB, etc.
I want to merge A and AA (and rest) as below:
arrayFinall = [
{
id: 'A-AA',
values: [
{date:"1/1/2022", measure1: 231, measure2: 23 },
{date: "1/2/2022", measure1: 31, measure2: 67}},
],
{
id: 'B-BB',
values: [
{date:"1/1/2020", measure1: 51, measure1: 90},
{date:"1/2/2020", measure1: 66, measure1: 100},
}
]
Either creating a new array that has both measures and the date for a new key A-AA
or
push measure from array2 into appropriate position in array 1 work in this case.
const array1 = [
{
id: 'A',
values: [
{ date: '1/1/2022', measure: 231 },
{ date: '1/2/2022', measure: 31 },
],
},
{
id: 'B',
values: [
{ date: '1/1/2020', measure: 51 },
{ date: '1/2/2020', measure: 66 },
],
},
];
const array2 = [
{
id: 'AA',
values: [
{ date: '1/1/2022', measure: 23 },
{ date: '1/2/2022', measure: 67 },
],
},
{
id: 'BB',
values: [
{ date: '1/1/2020', measure: 90 },
{ date: '1/2/2020', measure: 100 },
],
},
];
function mergeArrays(array1, array2) {
const result = [];
const keys = Object.keys(array1);
keys.forEach((key) => {
const array1Values = array1[key].values;
const array2Values = array2[key].values;
const values = [];
array1Values.forEach((value) => {
const date = value.date;
const measure1 = value.measure;
const measure2 = array2Values.find((value2) => value2.date === date).measure;
values.push({ date, measure1, measure2 });
});
result.push({ id: `${array1[key].id}-${array2[key].id}`, values });
});
return result;
}
console.log(JSON.stringify(mergeArrays(array1, array2), null, 2));

Get last element inside new array with key value from Object?

I have an object where each key have an array of multiple objects. I want to get only the last item from each array.
I tried to push only last element inside a new array but the problem is that now it doesn't say which object belong to which class.
let data = {
"classA": [
{
date: '01-01',
present: 49,
absent: 14
},
{
date: '02-01',
present: 39,
absent: 24
},
{
date: '03-01',
present: 35,
absent: 28
}
],
"classB": [
{
date: '01-01',
present: 49,
absent: 14
},
{
date: '02-01',
present: 39,
absent: 24
},
{
date: '03-01',
present: 35,
absent: 28
}
],
"classC": [
{
date: '01-01',
present: 49,
absent: 14
},
{
date: '02-01',
present: 39,
absent: 24
},
{
date: '03-01',
present: 35,
absent: 28
}
]
}
let newData = [];
for (let [key, value] of Object.entries(data)) {
newData.push(value[value.length - 1]);
}
console.log(newData);
Is there anyway to push class name inside each object and get result something like this.
[
{
"class": "ClassA",
"date": "03-01",
"present": 35,
"absent": 28
},
{
"class": "ClassB",
"date": "03-01",
"present": 35,
"absent": 28
},
{
"class": "ClassC",
"date": "03-01",
"present": 35,
"absent": 28
}
]
One way is to use Object.assign
newData.push(Object.assign({class:key}, value[value.length -1]))
But, more a modern method, using spread in object literals syntax makes it (I think) easier to read
newData.push({class:key, ...value[value.length - 1]});
let data = {"classA":[{"date":"01-01","present":49,"absent":14},{"date":"02-01","present":39,"absent":24},{"date":"03-01","present":35,"absent":28}],"classB":[{"date":"01-01","present":49,"absent":14},{"date":"02-01","present":39,"absent":24},{"date":"03-01","present":35,"absent":28}],"classC":[{"date":"01-01","present":49,"absent":14},{"date":"02-01","present":39,"absent":24},{"date":"03-01","present":35,"absent":28}]}
let newData = [];
for (let [key, value] of Object.entries(data)) {
newData.push({class:key, ...value[value.length - 1]});
}
console.log(newData);
let data = {
"classA": [
{
date: '01-01',
present: 49,
absent: 14
},
{
date: '02-01',
present: 39,
absent: 24
},
{
date: '03-01',
present: 35,
absent: 28
}
],
"classB": [
{
date: '01-01',
present: 49,
absent: 14
},
{
date: '02-01',
present: 39,
absent: 24
},
{
date: '03-01',
present: 35,
absent: 28
}
],
"classC": [
{
date: '01-01',
present: 49,
absent: 14
},
{
date: '02-01',
present: 39,
absent: 24
},
{
date: '03-01',
present: 35,
absent: 28
}
]
}
let newData = [];
for (let [key, value] of Object.entries(data)) {
newData.push(value[value.length - 1]);
newData[newData.length-1].class=key
}
console.log(newData);
You can do like this also
let result = Object.entries(data).map(data => {
return { class: data[0], ...data[1][data[1].length - 1] };
});

Get Specific Key with Highest Value from a (JSON) Object

I'm getting data back from an API response and attempting to get the name of the pitch with the highest speed. Here is a sample of the API response.
{
page: 1,
total_pages: 4,
listings: [
{
name: "A.J. Burnett",
pitches: [
{
name: "4 Seam FB",
speed: 96,
control: 84,
},
{
name: "Knuckle Curve",
speed: 79,
control: 74,
},
{
name: "Sinker",
speed: 95,
control: 64,
},
{
name: "Changeup",
speed: 81,
control: 44,
}
]
},
{
name: "Joe Smitch",
pitches: [
{
name: "4 Seam FB",
speed: 91,
control: 82,
},
{
name: "Changeup",
speed: 69,
control: 44,
}
]
},
]
}
Here is what I've tried:
itemSet.forEach( (item) => {
let fastestPitch = Object.keys(item.pitches).reduce((a, b) => {
item.pitches[a] > item.pitches[b] ? item.pitches[a].name : item.pitches[b].name
});
});
However, this always returns the name of the LAST pitch in the array. I'm attempting to return the pitch with the highest speed.
Edit: I've also tried the following, but it returns an error.
itemSet.forEach( (item) => {
let fastestPitch = Object.keys(item.pitches).reduce((a, b) => {
item.pitches[a].speed > item.pitches[b].speed ? item.pitches[a].name : item.pitches[b].name
});
});
Error:
(node:80698) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'speed' of undefined
You can do something like this:
const data = {
page: 1,
total_pages: 4,
listings: [{
name: "A.J. Burnett",
pitches: [{
name: "4 Seam FB",
speed: 96,
control: 84,
},
{
name: "Knuckle Curve",
speed: 79,
control: 74,
},
{
name: "Sinker",
speed: 95,
control: 64,
},
{
name: "Changeup",
speed: 81,
control: 44,
}
]
},
{
name: "Joe Smitch",
pitches: [{
name: "4 Seam FB",
speed: 91,
control: 82,
},
{
name: "Changeup",
speed: 69,
control: 44,
}
]
},
]
}
const fastesPitches = data.listings.map(({ pitches }) => {
return pitches.reduce((a, c) => c.speed > a.speed ? c : a).name;
});
console.log(fastesPitches);
To extract the fastest of each, you can Array#map each of the entries in listings and then Array#reduce their entries in pitches like this:
let data = { page: 1, total_pages: 4, listings: [{ name: "A.J. Burnett", pitches: [{ name: "4 Seam FB", speed: 96, control: 84, }, { name: "Knuckle Curve", speed: 79, control: 74, }, { name: "Sinker", speed: 95, control: 64, }, { name: "Changeup", speed: 81, control: 44, } ] }, { name: "Joe Smitch", pitches: [{ name: "4 Seam FB", speed: 91, control: 82, }, { name: "Changeup", speed: 69, control: 44, } ] }, ] };
let fastestPitches = data.listings.map(obj => {
return obj.pitches.reduce((best, current) => {
return best.speed > current.speed ? best : current
}, {}).name
});
console.log(fastestPitches)
Note that when you reduce, the first argument (best, in this case) is the result of the previous callback. So if you return just the name, you won't know what the speed of it was. So, you traverse and compare the speeds, then return the entire object that was better. When this finishes, you get the name of the result.
You could take a complete dynamic approach which looks for any depth and return the object with the wanted highest property from the most nested objects.
function getHighest(object, key) {
return Object.values(object).reduce((r, o) => {
if (!o || typeof o !== 'object') return r;
if (key in o && (!r || r[key] < o[key])) return o;
var temp = getHighest(o, key);
if (temp && (!r || r[key] < temp[key])) return temp;
return r;
}, undefined);
}
var data = { page: 1, total_pages: 4, listings: [{ name: "A.J. Burnett", pitches: [{ name: "4 Seam FB", speed: 96, control: 84 }, { name: "Knuckle Curve", speed: 79, control: 74 }, { name: "Sinker", speed: 95, control: 64 }, { name: "Changeup", speed: 81, control: 44 }] }, { name: "Joe Smitch", pitches: [{ name: "4 Seam FB", speed: 91, control: 82 }, { name: "Changeup", speed: 69, control: 44 }] }] },
highest = getHighest(data, 'speed');
console.log(highest.name);
console.log(highest);

Array.sort does not reorder array with NaN values in object

I have a simple case with reordering items in array by prop minLVL of each object in it.
But for some reason it works only if the previous and the next ones props are present (as you can see not each object has required minLVL field). So, if the minLVL is missing in some object it does not reoder such item to the bottom of the list, it just stay in the same position instead.
How can I solve it? Thanks
Example:
var h = [
{
ID: 172
},
{
ID: 179,
minLVL: "30"
},
{
ID: 169
},
{
ID: 173
},
{
ID: 167,
minLVL: "25"
},
{
ID: 175,
minLVL: "10"
}
]
var n = h.sort((a, b) => Number(b.minLVL) - Number(a.minLVL))
console.log(n)
You can default those values to 0 if it does not exist inside the sort function:
var h = [{
ID: 172
}, {
ID: 179,
minCrimeLevel: "30"
}, {
ID: 169
}, {
ID: 173
}, {
ID: 167,
minCrimeLevel: "25"
}, {
ID: 175,
minCrimeLevel: "10"
}]
var n = h.sort((a, b) => (Number(b.minCrimeLevel) || 0) - (Number(a.minCrimeLevel)|| 0))
console.log(n)

Is it possible to sort an array of objects in this specific way using sort()?

Sorry for not putting the specific sort in the title but I couldn't word it properly.
I'm sorting a list of file objects that have details such as name, date uploaded and category and I need to sort it in such a way that the last uploaded file is push to the top and the rest of them aren't sorted.
So for example, if I had the list sorted from oldest to newest in descending order I still want the newest one at the top.
The lists are already sorted in terms of category, date, etc. when returned from the database so I just need to further sort it after.
It has to be done in a function that gets passed to a sort().
There are a lot of solutions for this. As simplest solution you can use something like https://lodash.com/docs/4.17.4#orderBy
Array#sort is not necessarily stable, as long as you sort just by one property as in the first result.
You need another value for moving equal (resulting with 0) items to the right place, here with additional value of id, which are in creation order.
var data = [{ id: 0, date: '2017-01-01' }, { id: 2, date: '2017-01-01' }, { id: 5, date: '2017-01-02' }, { id: 6, date: '2017-01-05' }, { id: 9, date: '2017-01-06' }, { id: 19, date: '2017-01-11' }, { id: 24, date: '2017-02-01' }, { id: 67, date: '2017-02-01' }, { id: 80, date: '2017-02-11' }, { id: 90, date: '2017-02-21' }, { id: 101, date: '2017-02-23' }, { id: 149, date: '2017-02-28' }, { id: 163, date: '2017-03-01' }, { id: 190, date: '2017-03-02' }, { id: 321, date: '2017-03-05' }, { id: 444, date: '2017-03-17' }],
lastDate = data[data.length - 1].date;
data.sort(function (a, b) {
return (b.date === lastDate) - (a.date === lastDate);
});
console.log(data);
data.sort(function (a, b) {
return (b.date === lastDate) - (a.date === lastDate) || a.id - b.id;
});
console.log(data);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Another solution would be, to use Array#pop for the last item and Array#unshift for inserting at top place of the array.
var data = [{ id: 0, date: '2017-01-01' }, { id: 2, date: '2017-01-01' }, { id: 5, date: '2017-01-02' }, { id: 6, date: '2017-01-05' }, { id: 9, date: '2017-01-06' }, { id: 19, date: '2017-01-11' }, { id: 24, date: '2017-02-01' }, { id: 67, date: '2017-02-01' }, { id: 80, date: '2017-02-11' }, { id: 90, date: '2017-02-21' }, { id: 101, date: '2017-02-23' }, { id: 149, date: '2017-02-28' }, { id: 163, date: '2017-03-01' }, { id: 190, date: '2017-03-02' }, { id: 321, date: '2017-03-05' }, { id: 444, date: '2017-03-17' }];
data.unshift(data.pop());
console.log(data);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can achieve this by simply compound your sorting algorithm:
sortingLabels = ["date uploaded", "category", "date"]
myArray.sort((a,b) => {
for (l of sortingLabels) {
const comparison = compare(a,b,l)
if (comparison == 0) {continue}
return comparison
}
})
Now all you have to do is implement a compare(a,b,label) function that returns -1,1, or 0 based on the label.
Note: The for loop for-of works in ES2015+, which is most modern browsers and Node.js. However, if you need to support significantly older browsers, you might want to consider using a general for-loop

Categories

Resources