Use sort to sort by preference? - javascript

I have an array of objects that returns 3 possible sensitivity values: "LOW", "MEDIUM", "HIGH". However with the code below it is sorting in "HIGH - MEDIUM and LOW" respectively in ascending order and I wish it to return "HIGH - MEDIUM and LOW". What can I fix in this code?
In this function I compare the sensitivities received in the array
orderItemsByOrderOption = (items) => {
switch (this.state.selectedOrderOption.column) {
case "sensitivity":
return items.sort((a, b) => {
if (a.sensitivity > b.sensitivity) {
return 1;
}
if (a.sensitivity < b.sensitivity) {
return -1;
}
// a must be equal to b
return 0;
});

You could create an array with desired order and use the index of your array elements' sensitivity in this array for sorting.
Example
const arr = [
{ sensitivity: "MEDIUM" },
{ sensitivity: "LOW" },
{ sensitivity: "HIGH" }
];
const order = ["HIGH", "MEDIUM", "LOW"];
arr.sort((a, b) => order.indexOf(a.sensitivity) - order.indexOf(b.sensitivity));
console.log(arr);

There is a way is to convert rank into numeric value and compare that value
const data = [
{ id: 1, sensitivity: "LOW" },
{ id: 2, sensitivity: "HIGH" },
{ id: 3, sensitivity: "LOW" },
{ id: 4, sensitivity: "MEDIUM" },
{ id: 5, sensitivity: "HIGH" },
{ id: 6, sensitivity: "MEDIUM" },
];
const getRankInNum = (el) =>
({
HIGH: 2,
MEDIUM: 1,
LOW: 0,
}[el.sensitivity] || -1);
const res = data.sort((a, b) => getRankInNum(b) - getRankInNum(a));
console.log(res);

You could create a sort order lookup that maps HIGH, MEDIUM, and LOW to numeric values since sorting them alphabetically doesn't make sense.
const sensitivitySortOrder = {
HIGH: 0,
MEDIUM: 1,
LOW: 2
};
orderItemsByOrderOption = (items) => {
switch (this.state.selectedOrderOption.column) {
case "sensitivity":
return items.sort((a, b) => {
const aRank = sensitivitySortOrder[a.sensitivity];
const bRank = sensitivitySortOrder[b.sensitivity];
if (aRank > bRank) {
return 1;
}
if (aRank < bRank) {
return -1;
}
// a must be equal to b
return 0;
});

Related

Calculate average of an array with objects that has a nested Object

I am trying to loop through array of Objects and calculate the average of a nested Object containing several different keys.
This is the start array:
[{
course: "math",
id: 4,
values: {
2017: 8,
2018: 9
}
}, {
course: "math",
id: 4,
values: {
2017: 5,
2019: 7
}
}]
This is my goal:
{2017:6.5,2018:9,2019:7}
Now it returns correct for 2017 but NaN for 2018 and 2019. If anyone have better way of solving this that doesn't require so much please provide to.
This is what I have tried so far. I have been searching a lot but not really found anything I can use.
const testObject = [{
id: 4,
course: "math",
values: {
2017: 8,
2018: 9
}
},
{
id: 5,
course: "English",
values: {
2017: 8,
2018: 9
}
},
{
id: 4,
course: "math",
values: {
2017: 5,
2019: 7
}
},
{
id: 4,
course: "english",
values: {
2017: 5,
2019: 7
}
},
]
//First I filter out the id 4 and course Math
const mathid1 = testObject.filter((e) => e.id === 4 && e.course === "math");
//I than find all the different years
const ArrayOfAllYears = []
mathid1.map((element) => {
ArrayOfAllYears.push(Object.keys(element.values));
})
//I here find all the different years
const withDuplicates = ArrayOfAllYears.reduce(function(arrayOne, arrayTwo) {
return arrayOne.concat(arrayTwo);
}, []);
const withoutDuplicates = Array.from(new Set(withDuplicates));
//Here I just create the calculate average function
const Result = {}
const calculateAverage = (array) => {
const sum = array.reduce((a, b) => a + b);
return sum / array.length;
};
const newObj = {}
withoutDuplicates.map((year) => {
let reformattedArray = mathid1.map(obj => {
if (obj["values"][year]) {
return obj["values"][year]
}
})
newObj[year] = calculateAverage(reformattedArray)
})
console.log(newObj)
// I want to calculate the average of the mathid1 values and return it on a Object like {2017:..,2018..}
There are two simple steps to the problem.
First, you need to reduce the array to an object with years and values:
// this outputs
// { 2017: [8, 5], 2018: [9], 2019: [7] }
function byYear(array) {
// take each item of an array
return array.reduce((acc, data) => {
// take the values of that item
Object.entries(data.values).forEach(([year, value]) => {
// and map all the values to years
acc[year] = acc[year] || []
acc[year].push(value)
})
return acc
}, {})
}
The second step is just taking averages:
function average(object) {
const averages = {}
for (let key in object) {
averages[key] = object[key].reduce((sum, value) => sum + value) / object[key].length
}
return averages
}
And now you put them together:
average(byYear(input))
In here, the input is the filtered array. As a whole snippet:
function byYear(array) {
return array.reduce((acc, data) => {
Object.entries(data.values).forEach(([year, value]) => {
acc[year] = acc[year] || []
acc[year].push(value)
})
return acc
}, {})
}
function average(object) {
const averages = {}
for (let key in object) {
averages[key] = object[key].reduce((sum, value) => sum + value) / object[key].length
}
return averages
}
const output = average(byYear([{
course: "math",
id: 4,
values: {
2017: 8,
2018: 9
}
}, {
course: "math",
id: 4,
values: {
2017: 5,
2019: 7
}
}]))
console.log(output)
The problem with your current code lies in how you build the reformattedArray variable. First, notice that your map function implicitly returns undefined whenever that year is missing from the current object:
let reformattedArray = mathid1.map(obj => {
if (obj["values"][year]) {
return obj["values"][year]
}
// There is an implicit return undefined, right here...
})
When you use the array .map method, every item of the array will be replaced by the return value of the map function. In the case that the year is not present, it will not go into the if block, and so it implicitly returns undefined upon reaching the end of the function.
So, ultimately all you have to do is remove the undefined entries from this array, and your code will work as-is.
One way to do that is to just use .filter(Boolean) on the array, which removes any falsey entries (which undefined is). Eg:
let reformattedArray = mathid1.map(obj => {
/* code here */
}).filter(Boolean); // Note the filter here...
Here is your snippet with that modification:
const testObject = [{
id: 4,
course: "math",
values: {
2017: 8,
2018: 9
}
},
{
id: 5,
course: "English",
values: {
2017: 8,
2018: 9
}
},
{
id: 4,
course: "math",
values: {
2017: 5,
2019: 7
}
},
{
id: 4,
course: "english",
values: {
2017: 5,
2019: 7
}
},
]
//First I filter out the id 4 and course Math
const mathid1 = testObject.filter((e) => e.id === 4 && e.course === "math");
//I than find all the different years
const ArrayOfAllYears = []
mathid1.map((element) => {
ArrayOfAllYears.push(Object.keys(element.values));
})
//I here find all the different years
const withDuplicates = ArrayOfAllYears.reduce(function(arrayOne, arrayTwo) {
return arrayOne.concat(arrayTwo);
}, []);
const withoutDuplicates = Array.from(new Set(withDuplicates));
//Here I just create the calculate average function
const Result = {}
const calculateAverage = (array) => {
const sum = array.reduce((a, b) => a + b);
return sum / array.length;
};
const newObj = {}
withoutDuplicates.map((year) => {
let reformattedArray = mathid1.map(obj => {
if (obj["values"][year]) {
return obj["values"][year]
}
}).filter(Boolean)
newObj[year] = calculateAverage(reformattedArray)
})
console.log(newObj)
// I want to calculate the average of the mathid1 values and return it on a Object like {2017:..,2018..}
Group items by year.
Calculate average.
const items=[{
course: "math",
id: 4,
values: {
2017: 8,
2018: 9
}
}, {
course: "math",
id: 4,
values: {
2017: 5,
2019: 7
}
}]
const groupedValues=items.reduce((groupedValues,item)=>{
Object.entries(item.values).forEach(([year,value])=>{
if(groupedValues[year]){
groupedValues[year]={value:groupedValues[year].value+value,items:groupedValues[year].items+1};
} else {
groupedValues[year]={value,items:1};
}
});
return groupedValues;
},{})
console.log(groupedValues);
const result = Object.entries(groupedValues).reduce((result,item)=>{
result[item[0]]=item[1].value/item[1].items;
return result;
},{})
console.log(result);
I would recommend extracting the years information into a map:
/** #type {Map<string, number[]} */
const years = new Map();
testObject.forEach((obj) => {
Object.keys(obj.values).forEach((key) => {
if (!years.has(key)) years.set(key, []);
years.set(key, [...years.get(key), obj.values[key]]);
});
});
Then you can simply loop over the map and create the resulting object:
const result = {};
years.forEach((values, key) => {
Object.defineProperty(result, key, {
value: values.reduce((acc, val) => acc + val) / values.length,
enumerable: true,
});
});
console.log(result);
It should output:
{ '2017': 6.5, '2018': 9, '2019': 7 }

How can i compare two arrays of different length and keys on the basis of matching key values?

I have tried this, but it is giving the correct result
Array1
Array1: [
{
id: 2,
city: 'washington',
code: 0099,
room: 5,
...
},
{
...
},
...
]
Array 2
Array2: [
{
"id": 2,
"name": "john"
"number": 727625,
"etage": 5,
"status": 0,
...
},
{
...
},
...
]
My Code
let Result = [];
if (Array1 && Array1.length > 0 && Array2 && Array2.length > 0) {
Array1.forEach((arr1, index) => {
Array2.forEach((arr2, index) =>{
if (arr1.id === arr2.id && arr1.room === arr2.etage) {
Result.push(arr1)
}
})
})
}
console.log(Result)
What I want ?
I want items(objects) of Array1 by comparing both arrays, where
both have same id's && room from Array1's object equal to the etage
from Array2's object.
Please guide me, how can I do this in ES6 style in React js?
The main problem with nested loops is the unnecessary iteration of each element of the first array and multiple iterations of the second array.
This approach takes two loops, one for generating all keys from array2 and the other to filter array1.
You could take a Set for compound key of id and etage and filte the array for getting the items with same id and room.
const
getKey = (...values) => values.join('|'),
keys = new Set(array2.map(({ id, etage }) => getKey(id, etage))),
result = array1.filter(({ id, room }) => keys.has(getKey(id, room));
With condition
room > etage
and a Map.
const
ids = array2.reduce(
(m, { id, etage }) => m.set(id, Math.min(etage, m.get(id) || 0)),
new Map
),
result = array1.filter(({ id, room }) => room > ids.get(id));
I'd do something like this:
Array1= [
{
id: 2,
city: 'washington',
code: 0099,
room: 5,
}
];
Array2= [
{
"id": 2,
"name": "john",
"number": 727625,
"etage": 5,
},
];
const result = Array1.filter(a1 => Array2.find(a2 => a1.id == a2.id) && Array2.find(a2 => a1.room == a2.etage));
console.log(result);
That will return a filtered array by room, etage and id.
You can use filter and some ES6 methods:
const arr1 = [
{
id: 1,
room: 1
},
{
id: 2,
room: 5
},
{
id: 3,
room: 3
}
];
const arr2 = [
{
id: 0,
etage: 0
},
{
id: 2,
etage: 5
},
{
id: 3,
etage: 3
}
];
const getTheSame = (arr1, arr2) => {
return arr1.filter(o1 =>
arr2.some(o2 => o1.id === o2.id && o1.room === o2.etage)
);
};
console.log("Result: ", getTheSame(arr1, arr2));

JavaScript - sort 2 object arrays on the same field

In JavaScript I have 2 object arrays that have the same objects but are in a different order. I'm trying to figure out how to sort one array based on the order of the other. There is a unique field they both share (sortField below) I'm just failing on figuring out how to sort with it. Here's an example of my arrays:
sorter array:
[
{
"displayName": "Party",
"sortField": "com.uniqueXbd",
"elementId": "PtyListPanel"
}, {
"displayName": "Group",
"sortField": "com.uniqueARd",
"elementId": "GrpListPaneARd"
}, {
"displayName": "Leader",
"sortField": "com.uniqueEcF",
"elementId": "LeaderListPaneEcF"
}
]
needsSorted array:
[
{
"displayName": "Group",
"sortField": "com.uniqueARd",
"elementId": "GrpListPaneARd"
}, {
"displayName": "Leader",
"sortField": "com.uniqueEcF",
"elementId": "LeaderListPanel"
}, {
"displayName": "Party",
"sortField": "com.uniqueXbd",
"elementId": "PtyListPaneEcF"
}
]
I'm guessing it's going to look something like this?
needsSorted.sort((a, b) => {
if(sorter.sortField...){
return 1
})
Thanks
const output = [];
sortedArray.forEach( sortedItem => {
const matchingItem = unsortedArray.find( unsortedItem => unsortedItem.sortField === sortedItem.sortField );
if(matchingItem){
output.push(matchingItem);
}
});
Since you know the second array is the order you want the items from the first array to be in, you should loop through it. Then find the matching item from the first list, and push it into your output in that order.
You can make a sorting lookup that maps the sort key to the index in the original array. Then in your sort, you can look it up for both objects in the comparison.
This replaces the repeated need to lookup the index in the original array for each comparison with a constant time object lookup so it should be more performant for larger arrays at the expense of the space for the lookup object.
let sortObj = [{"displayName": "Party","sortField": "com.uniqueXbd","elementId": "PtyListPanel"}, {"displayName": "Group","sortField": "com.uniqueARd","elementId": "GrpListPaneARd"}, {"displayName": "Leader","sortField": "com.uniqueEcF","elementId": "LeaderListPaneEcF"}]
let needsSorted = [{"displayName": "Group","sortField": "com.uniqueARd","elementId": "GrpListPaneARd"}, {"displayName": "Leader","sortField": "com.uniqueEcF","elementId": "LeaderListPanel"}, {"displayName": "Party","sortField": "com.uniqueXbd","elementId": "PtyListPaneEcF"}]
let sortLookup = sortObj.reduce((obj, item, idx) => {
obj[item.sortField] = idx
return obj
}, {})
needsSorted.sort((a,b) => sortLookup[a.sortField] - sortLookup[b.sortField])
console.log(needsSorted)
var obj = [
{
"one": 1,
"two": 9
}, {
"one": 3,
"two": 5
}, {
"one": 1,
"two": 2
}
];
var obj = [
{
"one": 1,
"two": 2,
}, {
"one": 1,
"two": 9
}, {
"one": 3,
"two": 5
}
];
obj.sort(function(a, b) {
return a["one"] - b["one"] || a["two"] - b["two"];
});
const sortedIndexes = sorter.map(i => i.sortField);
needsSorted.sort((a, b) => {
const aIndex = sortedIndexes.findIndex((i) => i === a.sortField);
const bIndex = sortedIndexes.findIndex((i) => i === b.sortField);
return aIndex - bIndex;
})
Given that you just want to compare the two arrays and make sure they are still the same, I would go about it differently:
const first = sorted.sort((a, b) => a.localCompare(b))
const second = needsSorting.sort((a, b) => a.localCompare(b))
if (JSON.stringify(first) != JSON.stringify(second)) {
console.log("the array was modified!");
}
const sortOrder = sorted.map(item => item.sortField);
needsSorted.sort((a, b) => {
return sortOrder.indexOf(a.sortField) > sortOrder.indexOf(b.sortField) ? 1 : -1;
});
const fields = sorted.map(x => x.sortField);
const value = x => fields.indexOf(x.sortField);
needSorted.sort((a, b) => value(a) - value(b));
console.log(needSorted);
const sorted = [
{
displayName: "Party",
sortField: "com.uniqueXbd",
elementId: "PtyListPanel"
},
{
displayName: "Group",
sortField: "com.uniqueARd",
elementId: "GrpListPaneARd"
},
{
displayName: "Leader",
sortField: "com.uniqueEcF",
elementId: "LeaderListPaneEcF"
}
];
const needSorted = [
{
displayName: "Group",
sortField: "com.uniqueARd",
elementId: "GrpListPaneARd"
},
{
displayName: "Leader",
sortField: "com.uniqueEcF",
elementId: "LeaderListPanel"
},
{
displayName: "Party",
sortField: "com.uniqueXbd",
elementId: "PtyListPaneEcF"
}
];
const fields = sorted.map(x => x.sortField);
const value = x => fields.indexOf(x.sortField);
needSorted.sort((a, b) => value(a) - value(b));
console.log(needSorted);

JS findIndex method always returns -1

This:
const pageID: number = 4;
and this:
this.charts.findIndex((chart: IChart) => {
return chart.pageID === pageID;
}));
this.charts is an array of IChart[] which contains:
[
{
"pageID": 3,
"zoomable": false
},
{
"pageID": 4,
"zoomable": false
},
{
"pageID": 5,
"zoomable": false
}
]
Amazingly, this always returns -1. Even if I change the value of pageID to 4 or 5.
Usually this works, but it's driving me nuts. The only thing I am doing before trying to find the index is merging two arrays and removing duplicate values based on the pageID parameter, like this:
let unique = {};
this.charts = charts[0].concat(charts[1])
.filter((chart) => !unique[chart.pageID] && (unique[chart.pageID] = true))
.sort((a, b) => a.pageID - b.pageID);
The output of this.charts is the array pasted above with zoomable and pageID properties.
--
It's not rocket science even running the above in the proper sequence inside node returns the proper index which is 1 in my case. Does anyone have any insights on this issue?
Note: this is running in a Cordova app on iOS wkwebview.
Thank you.
Your code seems to run just fine.
const allCharts = [[
{ 'pageID': 3, 'zoomable': false },
{ 'pageID': 4, 'zoomable': false },
{ 'pageID': 5, 'zoomable': false }
], [
{ 'pageID': 3, 'zoomable': false },
{ 'pageID': 6, 'zoomable': false },
{ 'pageID': 7, 'zoomable': false }
]];
let unique = {};
const charts = allCharts[0].concat(allCharts[1])
.filter(chart => !unique[chart.pageID] && (unique[chart.pageID] = true))
.sort((a, b) => a.pageID - b.pageID);
const pageID = 4;
const idx = charts.findIndex(chart => {
return chart.pageID === pageID;
});
console.log(JSON.stringify(charts));
console.log('idx of pageId 4 is:', idx);

Javascript Arbitrary sort array based on values of a field

So I have an array of object which looks like this:
var myArray = [{priority : "low"}, {priority: "critical"}, {priority: "high"}]
I need to sort in this way: 1)Critical, 2) High and 3) Low.
how can this be done?
I suggest to use an object for the storing of the sort order.
If you need a default value for sorting, you could use a value for sorting unknown priority to start or to the end.
var sort = ['critical', 'high', 'low'],
defaultValue = Infinity,
sortObj = {},
myArray = [{ priority: "unknown" }, { priority: "low" }, { priority: "critical" }, { priority: "high" }];
sort.forEach(function (a, i) { sortObj[a] = i + 1; });
myArray.sort(function (a, b) {
return (sortObj[a.priority] || defaultValue) - (sortObj[b.priority] || defaultValue);
});
console.log(myArray);
Use an object that maps priority names to numbers, then sort based on that.
var priorities = {
low: 0,
high: 1,
critical: 2
};
var myArray = [{
priority: "low"
}, {
priority: "critical"
}, {
priority: "high"
}]
myArray.sort(function(a, b) {
return priorities[b.priority] - priorities[a.priority];
});
console.log(myArray);

Categories

Resources