Sort an object literal on its attribute value [duplicate] - javascript

This question already has answers here:
Sort array of objects by string property value
(57 answers)
Closed 4 years ago.
Below is my input, I would be looping through each of this element and sending each element to another function.
distinctParameterList = [{
careerGroupLevel: 'Analyst',
careerGroupCode: 130,
m06: 83,
m05: 82,
m08: 85,
fymcl: 'FY18|PromotionsOut|AN-10000100:CL 2',
m07: 84,
m09: 86,
intersectionId: '54697113|India|520|N'
},
{
careerGroupLevel: 'Analyst',
careerGroupCode: 130,
m06: 95,
m05: 94,
m08: 97,
fymcl: 'FY18|PromotionsOut|AN-10000110:CL 2',
m07: 96,
m09: 98,
intersectionId: '54697113|India|520|N'
},
{
careerGroupLevel: 'Analyst',
careerGroupCode: 130,
m06: 22,
m05: 21,
m08: 24,
fymcl: 'FY17|PromotionsOut|AN-10000100:CL 2',
m07: 23,
m09: 25,
intersectionId: '54697113|India|520|N'
},
{
careerGroupLevel: 'Analyst',
careerGroupCode: 130,
m06: 42,
m05: 41,
m08: 44,
fymcl: 'FY17|PromotionsOut|AN-10000110:CL 4',
m07: 43,
m09: 45,
intersectionId: '54697113|India|520|N'
}]
I am also calculating a value "calcCareerId " below :
let calcCareerId = fymclData.split("-")[1].split(":")[0];
Now i want to sort this structure on the basis of the "calcCareerId " value in fymcl attribute, so that i can get all the "10000100" values first and then "10000110".

Define the function:
let calcCareerId = fymclData => fymclData.split("-")[1].split(":")[0];
You can use lodash's sortBy function to sort in lexicographically ascending order. If you want descending order, just call .reverse on the result.
const result = _.sortBy(distinctParameterList, ({fymcl}) => calcCareerId(fymcl));
or use the native sort:
const result = distinctParameterList.sort((a, b) => {
const valueA = calcCareerId(a.fymcl);
const valueB = calcCareerId(b.fymcl);
return (valueA === valueB) ? 0 : (valueA < valueB ? 1 : -1)
})
Note that the native sort mutates the array and is not stable.

Related

Prevent arrray.map returning undefined when condition/callback is not met [duplicate]

This question already has answers here:
How to skip over an element in .map()?
(18 answers)
Map and filter an array at the same time
(16 answers)
Closed 1 year ago.
I was writing some code and something puzzled me. I have an array of numbers called alarmsList. Now I wish to iterate through this list and if a value is higher than 60 for example I want to create a new object array (collection) where we store the high value and it's index from the original array. So take the following code
const alarmsList = [1, 61, 77, 4, 5, 6, 7, 8, 85, 4, 3, 55];
const highAlarmsList = alarmsList.map((item, index) => {
if(item > 60) {
return ({ value: item, index })
}
});
console.log(highAlarmsList)
The console.log outputs the following
[
undefined,
{
"value": 61,
"index": 1
},
{
"value": 77,
"index": 2
},
undefined,
undefined,
undefined,
undefined,
undefined,
{
"value": 85,
"index": 8
},
undefined,
undefined,
undefined
]
This output is what I require but how do I prevent the undefined values being returned? I thought about using array.filter but that doesn't seem appropriate? Should I use a different array method? I don't want to use a for loop and push to a new array unless that is the best/only way to achieve the new array without the undefined values being returned.
You can use Array.filter() to removing the undefined values by using Boolean as the predicate:
const alarmsList = [1, 61, 77, 4, 5, 6, 7, 8, 85, 4, 3, 55];
const highAlarmsList = alarmsList.map((item, index) => {
if(item > 60) {
return ({ value: item, index })
}
}).filter(Boolean);
console.log(highAlarmsList)
You can use Array.flatMap() and return empty arrays instead of undefined, but that might effect performance for huge arrays:
const alarmsList = [1, 61, 77, 4, 5, 6, 7, 8, 85, 4, 3, 55];
const highAlarmsList = alarmsList.flatMap((item, index) =>
item > 60 ? { value: item, index } : []
);
console.log(highAlarmsList)
map creates a new array by running the callback on every element of the array. If the condition does not satisfy it will return undefined or null. So it not possible to skip the element from the output.
Alternatively you can ue reduce or filter
const alarmsList = [1, 61, 77, 4, 5, 6, 7, 8, 85, 4, 3, 55];
const highAlarmsList = alarmsList.reduce((acc, item, index) => {
item > 60 && acc.push({
value: item,
index
})
return acc;
}, [])
console.log(highAlarmsList)

How to dynamically calculate the array of object values in Javascript?

I have input format as below,
var boxplotInput = [{Day: "01-07-2021", "Thomas": 95, "Diana": 94, "Claura": 93, "Chandler": 93},
{Day: "02-07-2021", "Thomas": 95, "Diana": 94, "Claura": 94, "Chandler": 94},
...
...
{Day: "31-07-2021", "Thomas": 92, "Diana": 94, "Claura": 93, "Chandler": 91}];
I am quite new to javascript objects handling. I have written the code as below to calculate Q1, Q3, and median and it is working fine mathematically the way I am expecting.
//Getting the list of students (excluding date)
var keys;
for(var i = 0; i <boxplotInput.length; i++ ){
keys = Object.keys(boxplotInput[i]).slice(1);
}
////Here, I am hard-coding keys[0]. and getting "Thomas" data only. I am not getting how to avoid for one students only and achieve it for all students.
var studentDataSample = [];
for(var i = 0; i <boxplotInput.length; i++ ){
student1 = boxplotInput[i][keys[0]];
studentDataSample.push(student1);
}
studentDataSample.sort(function(a, b) {return a - b;});
var length = studentDataSample.length;//31
var midIndex = middleIndex(studentDataSample, 0, length);//16
var medianValue = studentDataSample[midIndex];
var Q1 = studentDataSample[middleIndex(studentDataSample, 0, midIndex)];
var Q3 = studentDataSample[middleIndex(studentDataSample, midIndex + 1, length)];
console.log(Q1+", "+medianValue+", "+Q3);// here, the values are fine.
function middleIndex(data, initial, length){
var n = length - initial + 1;
n = parseInt((n + 1) / 2);
return parseInt(n + initial);
}
Something, I understand that it could be achievable through the loop again.. but, not getting how to achieve it for all the students. Kindly, provide the suggestion or idea on this.
Thanks in advance.
if I understand you correctly all need following JS methods:
Array.reduce
Array.filter
Object.keys
The main thing you need here is create useful collection of students with their grades. After this you can calculate all the things you want. In this example I show how to calculate mean.
var boxplotInput = [
{Day: "01-07-2021", "Thomas": 95, "Diana": 94, "Claura": 93, "Chandler": 93},
{Day: "02-07-2021", "Thomas": 95, "Diana": 94, "Claura": 94, "Chandler": 94},
{Day: "31-07-2021", "Thomas": 92, "Diana": 94, "Claura": 93, "Chandler": 91}
];
/*
Get collection of students like:
{
Thomas: [ 95, 95, 92 ],
Diana: [ 94, 94, 94 ],
Claura: [ 93, 94, 93 ],
Chandler: [ 93, 94, 91 ]
}
*/
const students = boxplotInput.reduce((accumulator, currentDay) => {
const students = Object
.keys(currentDay)
.filter(el => el !== 'Day');
students.forEach(student => {
if (!accumulator[student]) {
accumulator[student] = [];
}
accumulator[student].push(currentDay[student]);
});
return accumulator;
}, {});
console.log('Student grades:', students);
// Then we can do anything with it
const studentNames = Object.keys(students);
// Example: finding mean
const studentMeans = studentNames.reduce((acc, student) => {
const grades = students[student];
const sumOfGrades = grades.reduce((acc, cur) => cur + acc, 0);
acc[student] = sumOfGrades / grades.length;
return acc;
}, {});
console.log('Means:', studentMeans);
/*
{
Thomas: 94,
Diana: 94,
Claura: 93.33333333333333,
Chandler: 92.66666666666667
}
*/
I will show you a very clean way to do this using Underscore. Let's inspect all the tools that Underscore and JavaScript provide for this purpose and build our solution one step at a time.
A nice function from Underscore is chain, which lets us massage data in a different shape step by step, while keeping the code very easy to read. For example, you can probably guess what the following chain will do:
var sortedLast = _.chain([2, 3, 1])
.sort()
.last();
console.log(sortedLast);
<script src="https://underscorejs.org/underscore-umd-min.js"></script>
chain creates a special wrapper around the input data, which has all Underscore functions as methods. Each method returns a new wrapper, so you can continue to apply more Underscore functions. At the end, you can unwrap the result by calling .value(). In some cases, like in the example above, this happens automatically. last returns the last element of an array.
A nice end shape, which we might want to work towards, could be the following:
{
Thomas: {min: 92, Q1: 93.5, median: 95, Q3: 95, max: 95},
Diana: {min: 94, Q1: 94, median: 94, Q3: 94, max: 94},
Claura: {min: 93, Q1: 93, median: 93, Q3: 93.5, max: 94},
Chandler: {min: 91, Q1: 92, median: 93, Q3: 93.5, max: 94},
}
This is an object which has the same keys as every element of boxplotInput, except for Day. Underscore has an omit function, which lets us do this cleanly, without having to rely on the keys appearing in a particular order:
_.chain(boxplotInput[0])
.omit('Day');
// {Thomas: 95, Diana: 94, Claura: 93, Chandler: 93}
Now we have an object with the correct keys, but wrong values.
mapObject lets us create a new object with the same keys but different values. Besides the input object, it takes a function which will be applied to every key-value pair of the input object in turn. That function takes the value as the first argument and the key as the second argument. Its return value becomes the value at the corresponding key in the new object.
As an intermediate step, let's create an object with a list of all scores per student:
{
Thomas: [95, 95, 92],
Diana: [94, 94, 94],
Claura: [93, 94, 93],
Chandler: [93, 94, 91],
}
In order to achieve this with mapObject, we need to write a function that, given the name of a student, returns an array with the student's scores. Its start will look like this:
function studentScores(firstScore, studentName) {
// code here
}
Let's look at an elegant way to get those scores. In your original code, you wrote something like this (but with key[0] instead of studentName):
var studentDataSample = [];
for (var i = 0; i < boxplotInput.length; i++) {
var student1 = boxplotInput[i][studentName];
studentDataSample.push(student1);
}
Underscore lets you get the same result in a very short line using map:
var studentDataSample = _.map(boxplotInput, studentName);
JavaScript's arrays nowadays have a built-in map method which lets you do something similar. It is not as flexible and concise as Underscore's map, but I'll show how to use it for completeness:
var studentDataSample = boxplotInput.map(dayScores => dayScores[studentName]);
We now know how to write our studentScores:
function studentScores(firstScore, studentName) {
return _.map(boxplotInput, studentName);
}
We don't need the firstScore, but we have to accept it as the first argument anyway, because we are going to pass this function to mapObject, which always passes the value first. Fortunately, we can just ignore it. We can write this function more concisely using the new arrow notation:
(fs, studentName) => _.map(boxplotInput, studentName)
Now we can include this function in our chain, in order to arrive at the intermediate result we previously discussed:
_.chain(boxplotInput[0])
.omit('Day')
.mapObject((fs, studentName) => _.map(boxplotInput, studentName));
// {
// Thomas: [95, 95, 92],
// Diana: [94, 94, 94],
// Claura: [93, 94, 93],
// Chandler: [93, 94, 91]
// }
Let's sort the scores as well, as a preparation for computing the quantiles:
_.chain(boxplotInput[0])
.omit('Day')
.mapObject((fs, studentName) => _.map(boxplotInput, studentName).sort());
// {
// Thomas: [92, 95, 95],
// Diana: [94, 94, 94],
// Claura: [93, 93, 94],
// Chandler: [91, 93, 94]
// }
We can add another mapObject to the chain in order to transform these arrays of sorted scores to the final {min, Q1, median, Q3, max} objects we were aiming for. Since this is not really what your question was about, I will just propose one possible way to do it in functional style:
// A function that returns a function (this is not a typo) that
// computes a particular quantile from a sorted array of numbers.
function quantile(fraction) {
return function(numbers) {
var middle = (numbers.length - 1) * fraction;
return (numbers[Math.floor(middle)] + numbers[Math.ceil(middle)]) / 2;
};
}
// A "blueprint" object with the keys we want to have, each having a
// function to compute the corresponding value from a sorted array of
// scores.
var quantileComputations = {
min: _.first,
Q1: quantile(.25),
median: quantile(.5),
Q3: quantile(.75),
max: _.last,
};
// A function that applies the above blueprint to a given array of
// numbers.
function getQuantiles(numbers) {
return _.mapObject(quantileComputations, f => f(numbers));
}
// Redefining the input data to make this snippet runnable.
var boxplotInput = [
{Day: "01-07-2021", "Thomas": 95, "Diana": 94, "Claura": 93, "Chandler": 93},
{Day: "02-07-2021", "Thomas": 95, "Diana": 94, "Claura": 94, "Chandler": 94},
{Day: "31-07-2021", "Thomas": 92, "Diana": 94, "Claura": 93, "Chandler": 91},
];
// Completing our chain using the above.
var statistics = _.chain(boxplotInput[0])
.omit('Day')
.mapObject((fs, studentName) => _.map(boxplotInput, studentName).sort())
.mapObject(getQuantiles)
.value();
console.log(statistics);
<script src="https://underscorejs.org/underscore-umd-min.js"></script>

How can I get the largest number value along with the username from this array?

Im trying to get the user & value with the highest number from this array but have had no luck in my searches. I'm starting to wonder if my array is poorly written.
{
"radtech2": 1,
"conorlarkin4": 25,
"jdon2001": 15,
"nobel_veo": 101,
"frapoden": 1,
"duckyboy17": 31,
"faeded": 30,
"jimbob20001": 17,
"leb0wski": 15,
"3cavalry": 2,
"hardoak22": 25,
"deep_slide": 10000,
"sillywil": 7
}
const users = {
"radtech2": 1,
"conorlarkin4": 25,
"jdon2001": 15,
"nobel_veo": 101,
"frapoden": 1,
"duckyboy17": 31,
"faeded": 30,
"jimbob20001": 17,
"leb0wski": 15,
"3cavalry": 2,
"hardoak22": 25,
"deep_slide": 10000,
"sillywil": 7
};
const highestUser = users => Object.keys(users).reduce(
(highest, current) => highest.val > users[current] ? highest : { user: current, val: users[current] },
{ user: undefined, val: -Infinity }
).user;
console.log(highestUser(users));
Use keys() and entries() methods to search your JSON object. Save largest value into e.g. const largest and then find out which key belongs to this value.
Let me try to squeeze it into a one-liner approach using Object.keys() and Array.reduce().
const users = {
"radtech2": 1,
"conorlarkin4": 25,
"jdon2001": 15,
"nobel_veo": 101,
"frapoden": 1,
"duckyboy17": 31,
"faeded": 30,
"jimbob20001": 17,
"leb0wski": 15,
"3cavalry": 2,
"hardoak22": 25,
"deep_slide": 10000,
"sillywil": 7
};
const res = Object.keys(users).reduce((a, b) => users[a] > users[b] ? a : b);
console.log(res);
How the above code works is that I get the array of keys from the users object, and I use reduce to get the highest possible value and return the corresponding property from the array obtained from Object.keys().
What you show in your question is an Object, not an Array; however, it does need to be turned into an array in order to work with it.
You can do that with Object.entries(), which will return an array of all the key/value pairs in the object.
Then you can use Array.reduce() to extract the one with the largest value.
const data = {
"radtech2": 1,
"conorlarkin4": 25,
"jdon2001": 15,
"nobel_veo": 101,
"frapoden": 1,
"duckyboy17": 31,
"faeded": 30,
"jimbob20001": 17,
"leb0wski": 15,
"3cavalry": 2,
"hardoak22": 25,
"deep_slide": 10000,
"sillywil": 7
}
let winner = Object.entries(data).reduce((a, b) => (a[1] > b[1]) ? a : b)
console.log(winner)

Extract data from an array of Object

I have this array of Object that I am getting from my database:
[Array of Object][1]
I would like to make an array of array for each value, but I can't manage to find a way to do it as I'm a beginner of javascript.
For example :
var Stats=[
[39,49,43,42,41,35], //SGW Value for each Object
[37,44,49,46,52,42], //UD Value for each Object
[8,11,8,8,16,15], //Virtual Value for each Object
...
]
The goal is to make a chart on chart.js that look like that :
[Chart Goal][2]
I would need to loop the dataset because I'll add more data and it would be way too long to set each dataset individually.
Thanks for your time.
You can do it like this:
let array1 = [
{
param1: 10,
param2: 20
},
{
param1: 30,
param2: 40
}
]
let array2 = array1.map(item => Object.values(item));
console.log(array2); // prints [[10, 20], [30, 40]]
First of all you need to create an array for each property you want to plot; i.e.:
var fsp = [],
msg = [],
sgw = [];
Then you can loop over your dataset and put the data in each array:
yourArray.forEach(function(obj){
//obj takes the value of each object in the database
fsp.push(obj.fsp);
msg.push(obj.msg);
sgw.push(obj.sgw);
})
or, if you are more familiar with for loop
for(var obj of yourArray){
fsp.push(obj.fsp);
msg.push(obj.msg);
sgw.push(obj.sgw);
}
Finally you can create an array as you pointed in your example
var result = [];
result.push(fsp, msg, sgw);
And the result will be
[
[89, 59, 43, 60, 81, 34, 28, 58, 75, 41],
[77, 91, 4, 56, 6, 1, 42, 82, 97, 18],
[24, 34, 4, 13, 75, 34, 14, 41, 20, 38]
]
For more informations take a look at Array.forEach(), Array.push() and for...of documentations
EDIT
As you pointed in your comment, you can generate arrays dynamically creating an object like var arrays = {};. Then in forEach(), or if for...of, you need to loop over objects with a for...in loop. The variable you declare in loop's head takes the value of index, numeric for Arrays, literal for Objects. You have to do something like:
yourArray.forEach(function(obj){
for(let index in obj){
if(!arrays[index]) // check if property has already been set and initialized
arrays[index] = []; // if not, it's initialized
arrays[index].push(obj[index]) // push the value into the array
}
})
Note that Object has been treated as Array because you access its properties with a variable filled at runtime.
The result will be:
arrays = {
fsp: [89, 59, 43, 60, 81, 34, 28, 58, 75, 41],
msg: [77, 91, 4, 56, 6, 1, 42, 82, 97, 18],
sgw: [24, 34, 4, 13, 75, 34, 14, 41, 20, 38]
}
To obtain only arrays use Object.values().
If you cannot imagine how this works, I suggest you to make some examples in Chrome Developer Tools' console, or in Node's console, or wherever you can have a realtime feedback, putting in the middle of code some console.log() of variables

Pairing multiple javascript arrays

I'm trying to solve a problem I have with multiple javascript arrays.
So basically the result I want is to match the arrays of a dropdown box with other values from other arrays that I will display.
The arrays contain different values, but the order is the most important thing
var array1 = [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22];
var array2 = [30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50];
var array3 = [36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56];
Let's say the user selects number 4, then I need to somehow select 32 in array2 and 38 in array3.
Any suggestions are gladly accepted, Thanks!
Get the index from the first array, with Array.prototype.indexOf
var index = array1.indexOf(4);
Get the values from other arrays with that index, like this
console.log(array2[index], array3[index]);
Note: If the value being searched is not found in the array, indexOf will not fail with an error but it will simply return -1. So, you might want to check before using that to access the elements from other arrays, like this
var index = array1.indexOf(4);
if (index !== -1) {
console.log(array2[index], array3[index]);
} else {
console.log("Invalid element selected");
}
Any time you have multiple parallel arrays, you should really consider refactoring it into a single array of objects. That way you never have to worry about keeping them synched. For example:
var myArray = [ { val1: 2, val2: 30, val3: 36 }, { val1: 4, val2: 32, val3: 38 }, ...];
Now to find the value for 4 you can simply do something like (although a simple for loop might be more efficient since you know there is only ever one result):
var myValues = myArray.filter(function(item) { return item.val1 === 4 });
And then access myValues[0].val2 and myValues[0].val3.
Or, if you are always looking up by the first value, you can use that as your key for an object that maps to your other two values. Something like:
var myArray = { 2: { val2: 30, val3: 36 }, 4: { val2: 32, val3: 38 },...};
Now if you want the other two values for 4 you can simply:
var value2 = myArray[4];
var value3 = myArray[4];
Assuming those are not only arrays and values, but you have actual <select> dropdown boxes:
Accessing the selected value is not only possible by using select1.value, but also by using select1.options[select1.selectedIndex].value. That.selectedIndex is what we are interested in, and you can use that equivalently on the option collections of the other two dropdowns, or the arrays with their values:
select2.options[select1.selectedIndex].value
array2[select1.selectedIndex]
select3.options[select1.selectedIndex].value
array3[select1.selectedIndex]
If you access them via the options collection you will need to make sure that one option is actually selected (select1.selectedIndex != -1), otherwise you'd get an exception.
Do it like this,
var valueFromSelect = 4;
var array1 = [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22];
var array2 = [30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50];
var array3 = [36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56];
for(var i = 0; i < array1.length; i++){
if(valueFromSelect == array1[i]){
console.log(array2[i], array3[i]);
break;
}
}
I suggest you don't use indexOf, it's not compatible with IE < 9 read more about that here indexOf MDN

Categories