ReactJS: Custom sort object by key rather than alphabetically - javascript

I have the following array:
{
"ALCOHOL": {
"items": [
{
"Category": "ALCOHOL",
"Name": "Beer - Can",
"Quantity": "1",
"Weight": 0.4,
"bought": false,
"packed": false,
"id": "748eeb60-15da-11ec-b278-b1ac8c1941a3"
},
{
"Category": "ALCOHOL",
"Name": "Gin & Tonic - Can",
"Quantity": "1",
"Weight": 0.27,
"bought": false,
"packed": false,
"id": "778f0c00-15da-11ec-b278-b1ac8c1941a3"
}
]
},
"BREAKFAST": {
"items": [
{
"Category": "BREAKFAST",
"Name": "Chobani Pouch 100g",
"Quantity": "1",
"Weight": "0.100",
"bought": false,
"packed": false,
"id": "KqoCRUAm0lDIszxbvCQ2"
},
{
"Category": "BREAKFAST",
"Name": "Milk - Fresh (1L)",
"Quantity": "1",
"Weight": 1.065,
"bought": false,
"packed": false,
"id": "75c5e4c0-15da-11ec-b278-b1ac8c1941a3"
}
]
}
}
I am using the following to display these items.
const categorizedDataFoods1 = foodValue.reduce((acc, curr) => {
const { id, Category, Name, Quantity, Weight, packingLocation, bought, packed } = curr;
if (!acc[Category]) {
acc[Category] = {
items: [],
};
}
acc[Category].items.push({ Category, Name, Quantity, Weight, packingLocation, bought, packed, id });
return acc;
}, {});
{Object.keys(categorizedDataFoods1).map((key, index) => {
return (
{categorizedDataFoods1[key]?.items
.sort(function (aa, ab) {
var categoryA = aa.Category.toUpperCase();
var categoryB = ab.Category.toUpperCase();
if (categoryA !== categoryB) {
return categoryA < categoryB ? -1 : 1;
}
var nameA = aa.Name.toUpperCase();
var nameB = ab.Name.toUpperCase();
if (nameA !== nameB) {
return nameA < nameB ? -1 : 1;
}
})
.map((checkbox, index) => {
{checkbox.Name}
}})})}
How can I custom sort these categories so in this example, Breakfast is shown before Alcohol. I also have other categories such as Lunch and Dinner - so I would want to display the items in the order of Breakfast > Lunch > Dinner > Alcohol etc

As suggested by #Ben Wong, having your own list of categories in sorted order is the best choice here.
['BREAKFAST', 'LUNCH', "DINNER', 'ALCHOL'].map(key => categorizedDataFoods1[key].items...
Now, for each category, you can have a sort function to sort by name or whatever you wish to.

Referencing our discussion in the comments, I've rewritten your code example below with as few changes as I can think of:
const categorizedDataFoods1 = foodValue.reduce((acc, curr) => {
const { id, Category, Name, Quantity, Weight, packingLocation, bought, packed } = curr;
if (!acc[Category]) {
acc[Category] = {
items: [],
};
}
acc[Category].items.push({ Category, Name, Quantity, Weight, packingLocation, bought, packed, id });
return acc;
}, {});
const sortedCategories = ['BREAKFAST', 'LUNCH', 'DINNER', 'ALCOHOL'];
{sortedCategories.map((key, index) => {
return (
{categorizedDataFoods1[key]?.items.slice()
.sort(function (aa, ab) {
var categoryA = aa.Category.toUpperCase();
var categoryB = ab.Category.toUpperCase();
if (categoryA !== categoryB) {
return sortedCategories.indexOf(categoryA) - sortedCategories.indexOf(categoryB);
}
var nameA = aa.Name.toUpperCase();
var nameB = ab.Name.toUpperCase();
if (nameA !== nameB) {
return nameA < nameB ? -1 : 1;
}
return 0;
})
.map((checkbox, index) => {
{checkbox.Name}
}})})}

Related

Return additional properties using reduce and spread operator JS?

I am using the reduce function below to count how many times a players name is mentioned and then list them based on who was mentioned the most to the least.
I am trying to return the 2nd property [`${value.subtitles[0].name} + ${index}`] : value.subtitles[0].url with my object and sort it. However it is not sorting properly. When only returning the first property [value.title]: (acc[value.title] || 0) + 1, everything works as intended. But the second property is making it sort incorrectly. It is supposed to be sorting based on the title property value which is an integer of how many times that player was mentioned, from most to least. Why is this happening?
Thanks for the help!
const players = [
{
"title": "Mike",
"titleUrl": "https://mikegameplay",
"subtitles": [
{
"name": "Mike Channel",
"url": "https://channel/mike"
}
]
},
{
"title": "Cindy",
"titleUrl": "https://cindy",
"subtitles": [
{
"name": "Cindy Channel",
"url": "https://channel/cindy"
}
]
},
{
"title": "Mike",
"titleUrl": "https://mike",
"subtitles": [
{
"name": "Mike Channel",
"url": "https://channel/mike"
}
]
},
{
"title": "Haley",
"titleUrl": "https://Haley",
"subtitles": [
{
"name": "Haley Channel",
"url": "https://channel/haley"
}
]
},
{
"title": "Haley",
"titleUrl": "https://Haley",
"subtitles": [
{
"name": "Haley Channel",
"url": "https://channel/haley"
}
]
},
{
"title": "Haley",
"titleUrl": "https://Haley",
"subtitles": [
{
"name": "Haley Channel",
"url": "https://channel/haley"
}
]
}
]
const counts = players.reduce((acc, value, index) => ({
...acc,
[value.title]: (acc[value.title] || 0) + 1,
[`${value.subtitles[0].name} + ${index}`] : value.subtitles[0].url
}), {});
const sortedValues = [];
for (const value in counts) {
sortedValues.push([value, counts[value]]);
};
sortedValues.sort((a, b) => b[1] - a[1]);
console.log(sortedValues)
try this
var groupBy = function (xs, key) {
return xs.reduce(function (rv, x) {
(rv[x[key]] = rv[x[key]] || []).push(x);
return rv;
}, {});
};
var pl = groupBy(players, "title");
console.log(pl);
let sortable = [];
for (var item in pl) {
sortable.push([item, pl[item].length, pl[item][0].subtitles[0].url]);
}
sortable.sort(function (a, b) {
return b[1] - a[1];
});
console.log(JSON.stringify(sortable));
result
[["Haley",3,"https://channel/haley"],["Mike",2,"https://channel/mike"],["Cindy",1,"https://channel/cindy"]]

how to get max value from a nested json array

I have a nested json array and I am trying to get the maximum value of the points attribute in this array.
data = {
"name": "KSE100",
"children": [
{
"name": "TECHNOLOGY & COMMUNICATION",
"children": [
{
"name": "TRG",
'points': -21
},
{
"name": "SYS",
},
]
},
{
"name": "OIL",
"children": [
{
"name": "PPL",
'points': 9
},
{
"name": "PSO",
'points': -19
},
]
},
]
}
I want the max value of points from under the children sections. I mean from under technology and oil sectors.
What I've done so far:
var max;
for (var i in data.children.length) {
for (var j in data.data[i]) {
var point = data.data[i].children[j]
}
}
Try the following:
data = {
"name": "KSE100",
"children": [
{
"name": "TECHNOLOGY & COMMUNICATION",
"children": [
{
"name": "TRG",
'points': -21
},
{
"name": "SYS",
},
]
},
{
"name": "OIL",
"children": [
{
"name": "PPL",
'points': 9
},
{
"name": "PSO",
'points': -19
},
]
},
]
}
var array = [];
for (var first of data.children) {
for (var second of first.children) {
if(second.points != undefined)
{
array.push(second);
}
}
}
var maximumValue = Math.max.apply(Math, array.map(function(obj) { return obj.points; }));
console.log(maximumValue);
you can use the reduce method on the array object to do this
const maxValues = []
data.children.forEach(el => {
if (el.name === 'OIL' || el.name === 'TECHNOLOGY & COMMUNICATIO'){
const max = el.children.reduce((current, previous) => {
if (current.points > previous.points) {
return current
}
}, 0)
maxValues.append({name: el.name, value: max.points})
}
})
This will give you an array of the objects with the name and max value.
First you can convert your object to a string through JSON.stringify so that you're able to use a regular expression
(?<=\"points\":)-?\\d*
To matchAll the values preceded by the pattern \"points\": that are or not negative values. After it, convert the result to a array through the spread operator ... and then reduce it to get the max value.
const data = {name:"KSE100",children:[{name:"TECHNOLOGY & COMMUNICATION",children:[{name:"TRG",points:-21},{name:"SYS"}]},{name:"OIL",children:[{name:"PPL",points:9},{name:"PSO",points:-19}]}]};
console.log(
[ ...JSON.stringify(data).matchAll('(?<=\"points\":)-?\\d*')]
.reduce((acc, curr) => Math.max(curr, acc))
)
I wasn't 100% sure, what your exact goal is, so I included a grouped max value and and overall max value with a slight functional approach.
Please be aware that some functionalities are not working in older browsers i.e. flatMap. This should anyways help you get started and move on.
const data = {
name: "KSE100",
children: [
{
name: "TECHNOLOGY & COMMUNICATION",
children: [
{
name: "TRG",
points: -21,
},
{
name: "SYS",
},
],
},
{
name: "OIL",
children: [
{
name: "PPL",
points: 9,
},
{
name: "PSO",
points: -19,
},
],
},
],
};
const maxPointsByGroup = data.children.reduce(
(acc, entry) => [
...acc,
{
name: entry.name,
max: Math.max(
...entry.children
.map((entry) => entry.points)
.filter((entry) => typeof entry === "number")
),
},
],
[]
);
console.log("grouped max:", maxPointsByGroup);
const overallMax = Math.max(
...data.children
.flatMap((entry) => entry.children.flatMap((entry) => entry.points))
.filter((entry) => typeof entry === "number")
);
console.log("overall max:", overallMax);

Javascript - Get occurence of json array with ESLINT 6

I can't set up an algo that counts my occurrences while respecting ESlint's 6 standards in javascript.
My input table is :
[
{
"id": 2,
"name": "Health",
"color": "0190fe"
},
{
"id": 3,
"name": "Agriculture",
"color": "0190fe"
},
{
"id": 1,
"name": "Urban planning",
"color": "0190fe"
},
{
"id": 1,
"name": "Urban planning",
"color": "0190fe"
}
]
And i want to get :
{"Urban planning": 2, "Health": 1, ...}
But that does not work with ESLINT / REACT compilation...
This is my code :
const jsonToIterate = *'MyPreviousInputJson'*
const names = []
jsonToIterate.map(item => (names.push(item.name)))
const count = []
names.forEach(item => {
if (count[item]){
count.push({text: item, value: 1})
} else {
count.forEach(function(top){top.text === item ? top.value =+ 1 : null})
}
})
Thank you so much
Well, you want an object in the end, not an array, so count should be {}. I also wouldn't use map if you're not actually returning anything from the call. You can use reduce for this:
let counts = topicsSort.reduce((p, c, i, a) => {
if (!p.hasOwnProperty(c.name)) p[c.name] = 0;
p[c.name]++;
return p;
}, {});
I'm half exppecting someone to close this as a duplicate because all you've asked for is a frequency counter. But here's an answer anyway:
const jsonToIterate = *'MyPreviousInputJson'*;
const names = {};
jsonToIterate.map(obj => {
if(obj.name in names){
names[obj.name]++
}
else{
names[obj.name] = 1;
}
})

Loop through possible arrays in json with typescript

I'm not asking how to loop through an array in typescript. My question is a bit different so let me explain first.
I have a json which looks like this:
{
"forename": "Maria",
"colors": [
{
"name": "blue",
"price": 10
},
{
"name": "yellow",
"price": 12
}
],
"items": [
{
"name": "sword",
"price": 20
}
],
"specialPowers": [
{
"name": "telekinesis",
"price": 34
}
]
},
{
"forename": "Peter",
"colors": [
{
"name": "blue",
"price": 10
}
],
"items": [
{
"name": "hat",
"price": 22
},
{
"name": "hammer",
"price": 27
}
]
}
// some more persons
As you can see, I have persons which can have arrays like colors, items or specialPowers. BUT a person can also have none of them. As you can see Maria has the array specialPowers, but Peter has not.
I need a function which checks if a person has one of these arrays and if so, I have to sum its price to a total. So I want the total price of all the things a person has.
At the moment I have three functions which basically look like this:
getTotalOfColors(person) {
let total = 0;
if(person.colors)
for (let color of person.colors) {
total = total + color.price;
}
return total;
}
getTotalOfItems(person) {
let total = 0;
if(person.items)
for (let item of person.items) {
total = total + item.price;
}
return total;
}
// SAME FUNCTION FOR SPECIALPOWERS
I basically have the same function for three times. The only difference is, that I'm looping through another array. But these functions do all the same. They first check, if the person has the array and secondly they loop through this array to add the price to a total.
Finally to my question: Is there a way to do this all in ONE function? Because they all are basically doing the same thing and I don't want redundant code. My idea would be to loop through all the arrays while checking if the person has the array and if so, adding its price to the total.
I assume the function would look something like this:
getTotal(person) {
let total = 0;
for (let possibleArray of possibleArrays){
if(person.possibleArray )
for (let var of person.possibleArray ) {
total = total + var.price;
}
}
return total;
}
Like this I would have a "universal" function but for that I have to have an array of the possible arrays like this: possibleArrays = [colors, items, specialPowers]
How do I achieve this? How and where in my code should I make this array ? Or is there even a better solution for this problem?
I created a function that seems to do the trick:
function totalPrice(data) {
let total = 0;
for (person of data) { //Go through the array of people
for (prop in person) { //Go through every property of the person
if (Array.isArray(person[prop])) { //If this property is an array
for (element of person[prop]) { //Go through this array
//Check if `price` is a Number and
//add it to the total
if (!isNaN(element.price)) total += element.price;
}
}
}
}
return total;
}
Demo:
function totalPrice(data) {
let total = 0;
for (person of data) {
for (prop in person) {
if (Array.isArray(person[prop])) {
for (element of person[prop]) {
if (!isNaN(element.price)) total += element.price;
}
}
}
}
return total;
}
let data = [
{
"forename": "Maria",
"colors": [{
"name": "blue",
"price": 10
},
{
"name": "yellow",
"price": 12
}
],
"items": [{
"name": "sword",
"price": 20
}],
"specialPowers": [{
"name": "telekinesis",
"price": 34
}]
},
{
"forename": "Peter",
"colors": [{
"name": "blue",
"price": 10
}],
"items": [{
"name": "hat",
"price": 22
},
{
"name": "hammer",
"price": 27
}
]
}
];
console.log(totalPrice(data));
You can use the function reduce and the function includes to select the desired targets.
var inputData = [{ "forename": "Maria", "colors": [{ "name": "blue", "price": 10 }, { "name": "yellow", "price": 12 } ], "items": [{ "name": "sword", "price": 20 }], "specialPowers": [{ "name": "telekinesis", "price": 34 }] }, { "forename": "Peter", "colors": [{ "name": "blue", "price": 10 }], "items": [{ "name": "hat", "price": 22 }, { "name": "hammer", "price": 27 } ] }];
function totalize(possibleArrays, data) {
return data.reduce((a, c) => {
return a + Object.keys(c).reduce((ia, k) => {
if (possibleArrays.includes(k)) c[k].forEach(p => ia += p.price);
return ia;
}, 0);
}, 0);
}
var total = totalize(["colors", "items", "specialPowers"], inputData);
console.log(total);
Something like this should also do it, I just logged the results in console, but you can do pretty much what you want with them :
const getSum = (person, prop) => {
let total = 0;
if(person[prop])
for (let value of person[prop]) {
total = total + value.price;
}
return total;
}
const props = ['colors', 'items', 'specialPowers']
console.log(data.map(person => props.map(prop => getSum(person, prop))));
Edit
I didn't get that you wanted to sum up all your properties for one person at once, this code is what I definitely what I would go for :
const sum = (a, b) => a + b;
const props = ['colors', 'items', 'specialPowers']
data.map(person =>
props.map(prop =>
(person[prop] || [])
.map(({price}) => price)
.reduce(sum, 0)
).reduce(sum, 0)
)
And if you want to sum all person's total price :
data.map(person =>
props.map(prop =>
(person[prop] || [])
.map(({price}) => price)
.reduce(sum, 0)
).reduce(sum, 0)
).reduce(sum, 0)

Load a JSON document and sort it on one or more values in key:value pairs with JavaScript

I want to load an external JSON object and sort based on one or more key:value pairs.
For example using the JSON below I might need to sort on category1 and then type.
I've tried array.sort() but no matter what I throw at it my data is never sorted; It's always output in the same order as in the JSON file.
{
"books": [
{
"sku": "{1234}",
"title": "Moby Dick",
"type": "paperback",
"category1": "fiction",
"category2": "classic",
"category3": "",
"image": "",
"ctaLabel": "Learn More"
},
{
"sku": "{5678}",
"title": "1984",
"type": "hardcover",
"category1": "fiction",
"category2": "",
"category3": "",
"image": "",
"ctaLabel": "Learn More"
},
{
"sku": "{5678}",
"title": "Another Title",
"type": "paperback",
"category1": "nonfiction",
"category2": "youngadult",
"category3": "",
"image": "",
"ctaLabel": "Learn More"
}
]
}
$(function() {
$.getJSON('books.json', function (data) {
console.log(data);
var items = data.books.map(function (item) {
return item.sku + ': ' + item.title;
});
if (items.length) {
var content = '<li>' + items.join('</li><li>') + '</li>';
var list = $('<ul />').html(content);
$("#show-data").html(list);
}
});
});
Based on this answer, you can implement a multi-level sort as follows :
function multiLevelSort(arr, criteria) {
return arr.sort(function(x, y) {
return criteria.reduce(function(prev, curr) {
var dir = (curr.dir < 0) ? -1 : 1,
x_ = x[curr.prop],
y_ = y[curr.prop];
return prev || (x_ === y_ ? 0 : x_ > y_ ? dir : -dir);
}, 0);
});
}
or, with destructuring (in Node but not yet in all browsers) :
function multiLevelSort(arr, criteria) {
return arr.sort(function(x, y) {
return criteria.reduce(function(prev, {prop, dir}) {
dir = (dir < 0) ? -1 : 1;
var x_ = x[prop],
y_ = y[prop];
return prev || (x_ === y_ ? 0 : x_ > y_ ? dir : -dir);
}, 0);
});
}
where :
arr is an array of objects, as in the question.
criteria is an array of objects of the following format :
var criteria = [
{prop: "type", dir: 1}, // dir:1=ascending; dir:-1=descending
{prop: "category1", dir: 1},
{prop: "category2", dir: 1}
];
Then simply call :
multiLevelSort(myArray, myCriteria);
Like Array.prototype.sort(), myArray will be mutated and returned.
DEMO

Categories

Resources