Order by nested object property inside a array - javascript

I'm stuck on this one thing trying to take top 3 manufacturers by numberOfCars, and, to do the same with cars: [], take top 3 by numberoOfCars
audi: {
cars: [],
collectionName: '',
numberOfCars: 0
}
I can do the first level with lodash like
_.take(_.orderBy(modelData, 'numberoOfCars', 'desc'), 3)
but I'm lost on doing the same on cars array as mentioned.
const mock = []
for (let i = 0; i < 140; i++) {
let manufacturerName
let name
if (i < 20) {
manufacturerName = 'Audi'
name = 'A6'
} else if (i > 19 && i < 40) {
manufacturerName = 'BMW'
name = '420 GC'
} else if (i > 19 && i < 40) {
manufacturerName = 'Mercedes'
name = 'AMG'
} else if (i > 39 && i < 60) {
manufacturerName = 'Mazda'
name = '6'
} else if (i > 59 && i < 80) {
manufacturerName = 'Volvo'
name = 'V90'
} else if (i > 79 && i < 100) {
manufacturerName = 'Renault'
name = 'Model'
} else if (i > 99 && i < 120) {
manufacturerName = 'Lamborghini'
name = 'Aventador'
} else if (i > 119 && i < 140) {
manufacturerName = 'Volkswagen'
name = 'Golf'
}
mock.push({
id: i,
name: name,
displayName: 'display-name ' + Math.floor(Math.random() * 10),
manufacturer: manufacturerName,
numberoOfCars: Math.floor(Math.random() * 100000),
})
}
const dataModel = mock.reduce((accumulator, currentValue) => {
const key = currentValue.manufacturer
if (accumulator[key] === undefined) {
accumulator[key] = {
collectionName: '',
numberoOfCars: 0,
cars: []
}
}
if (accumulator[key].collectionName === '') {
accumulator[key].collectionName = currentValue.manufacturer
}
if (currentValue.numberoOfCars !== undefined) {
accumulator[key].numberoOfCars += currentValue.numberoOfCars
}
if (currentValue.numberoOfCars !== undefined) {
accumulator[key].cars.push({
name: currentValue.name,
numberOfCars: currentValue.numberoOfCars
})
}
return accumulator
}, {})
console.log(dataModel)

Iterate over the keys in the outer object, order those keys by the number of cars associated with that key in the outer then pick the first 3 keys.
Then, reduce each key into an object that clones the value associated with that key and orders the cars in that value and takes the first 3:
var filtered = _.chain(dataModel)
.keys()
.orderBy(k=>dataModel[k].numberOfCars, 'desc')
.take(3)
.reduce((coll,k)=>{
coll[k] = _.clone(dataModel[k]);
coll[k].cars = _.chain(coll[k].cars)
.orderBy('numberOfCars', 'desc')
.take(3)
.value();
return coll;
}, {})
.value();
Example on jsfiddle
Credit to this answer by VLAZ for help on how to filter an object's items by an ordering over its values.

I guess it should be something like this:
const cars = Object.keys(dataModel).reduce((acc, item) => {
const collection = dataModel[item]
return acc.concat(collection.cars)
}, []);
const top3cars = _.take(_.orderBy(cars, 'numberOfCars', 'desc'), 3);

Related

how to set xyz value on areachart nextjs when has different length output?

i have an output:
categories: (2) ['2022-07','2022-08']
resultBilGol: (7) ['2B - Rumah Tangga Menengah', '3A - Niaga Kecil', '3B - Niaga Besar', 'R5 - Instansi Pemerintah', 'IA - Rumah Tangga', '2B - Rumah Tangga Menengah', 'R5 - Instansi Pemerintah']
resultTotal: (7) ['57000', '76700', '172000', '429300', '272000', '50200', '1467000']
but the output is like this
enter image description here
basicly , length 1-5 for resultBilGol and resultTotal is for categories (1) and length 6-7 is for categories (2)
So i want make the output is gonna like this:
2022-07 has value: 2B + 57000, 3A + 76700, 3B + 172000, R5 + 429300, IA + 272000
2022-08 has value: 2B + 50200, R5 + 1467000
this is my code to generate the output from back:
let categories = [];
let result = 0;
let resultGolongan = "";
let resultTotal = [];
let resultBilGol = [];
let res = "";
for (let i = 0; i < responseBillingTarget.length; i++) {
const momentAll = moment(responseBillingTarget[i]?.periode).format(
"YYYY-MM"
);
categories.push(moment(responseBillingTarget[i].periode).format("YYYY-MM"));
for (let index = 0; index < responseBillingGolongan.length; index++) {
const element = responseBillingGolongan[index];
const momentBilGol = moment(element.periode).format("YYYY-MM");
if (momentAll === momentBilGol) {
resultTotal.push(element.total);
resultBilGol.push(element.golongan);
}
}
}
and it is from tsx:
let seriesActive =
arrayBilGolongan === undefined
? []
: arrayBilGolongan.resultBilGol === undefined ||
arrayBilGolongan.resultBilGol === []
? []
: arrayBilGolongan.resultBilGol;
let seriesActive2 = arrayBilGolongan?.resultTotal;
let dataSeriesTotal = seriesActive2?.map((items: any, i: any) => {
return { items };
});
let dataSeriesGolongan = seriesActive.map((item: any, i: any) => {
let result = [];
for (let index = 0; index < dataSeriesTotal.length; index++) {
const element = dataSeriesTotal[index];
result.push(element.items);
}
if (
arrayBilGolongan.resultBilGol !== undefined ||
arrayBilGolongan.resultBilGol !== []
) {
return {
name: `${item}`,
data: result,
};
} else {
return {
name: `${item}`,
data: [0],
};
}
});
how to make my problem can be solve like this? please help me

loop through this Object that contain Array as values and combine all values, sort them, reindex and update values

I have to loop through the object values and sort them in such a way that I remove number 1 & 2 (lets call it index) from Self and Spouse. Then reindex Child3 and Child4 to Child1 and Child2.
Though the Object got from API response looks like below it makes more sense to reindex them as I'll be displaying this information on the screen.
Object looks like below: This is just a sample data. This object is
dynamically created based on User household information
eligibilityMap: {
"CHIP": [
"CHILD3" // should be Child1
],
"APTC/CSR": [
"SELF1", //should be Self
"SPOUSE2", //should be Spouse
"CHILD4" //should be Child2
]
}
My question is how should I loop through this Object and just sort the child and reindex the same?
Expected Result:
newMapping: {
"CHIP": ["Child1"],
"APTC/CSR": ["Self, Spouse and Child2"]
}
CODE:
var sortedMember = Object.keys(eligibilityMap).reduce((acc, key) => {
//fetch the array for the given key
var array = eligibilityMap[key];
console.log('array', array);
var sortedArray = array.sort( function (firstValue, secondValue) {
if (firstValue.indexOf("SELF") >= 0) return -1;
if (secondValue.indexOf("SELF") >= 0) return 1;
if (firstValue.indexOf("SPOUSE") >= 0) return -1;
if (secondValue.indexOf("SPOUSE") >= 0) return 1;
return 0;
});
console.log("sortedArray", sortedArray)
acc[key] = sortedArray;
return acc;
}, {})
$scope.memberString = Object.keys(sortedMember).reduce(function (acc, key) {
var array = sortedMember[key]
var formattedString = array.reduce(function (memberAcc, member, index) {
if (member.indexOf("SELF") >= 0) {
return "Applicant"
}
if (member.indexOf("SPOUSE") >= 0) {
var delimiter = index > 0 ? ", " : "";
return memberAcc + delimiter + "Spouse"
}
if(index === 0) return member;
var delimiter = index === array.length - 1 ? " and " : ", ";
return memberAcc + delimiter + member //CHILD1
}, "");
console.log("STRING", key, formattedString)
acc[key] = formattedString;
return acc;
}, {});
RESULT: (But still not what I wanted)
MEMBERS STRING {CHIP: "CHILD4", APTC/CSR: "Applicant, Spouse, CHILD3 and CHILD5"}
You can check this approach. I've created a dataMapper to map the specific data with their new values.
var obj = {
"CHIP": [
"CHILD3", // should be Child1
],
"APTC/CSR": [
"SELF1", //should be Self
"SPOUSE2", //should be Spouse
"CHILD4" //should be Child2
],
"TEST": [
"SELF1",
"CHILD3"
]
};
// Code to support IE9
var i = 0;
var updatedObj = {};
var data = Object.keys(obj).map(function(key) {
var values = obj[key];
var memberArr = new Array();
var dataObj = values.reduce(function(store, value) {
var storeKey = value.replace(/[0-9]/g, '');
var number = Number(value.replace(/^\D+/g, ''));
if(storeKey !== 'SELF' && storeKey !== 'SPOUSE') {
var arr = new Array();
for(i = 0; i < number; i++) {
arr.push(storeKey);
memberArr.push(storeKey);
}
store[storeKey] = arr;
} else {
var anotherArr = new Array(storeKey);
memberArr.push(storeKey);
store[storeKey] = anotherArr;
}
return store;
}, {});
memberArr.sort(function(a, b) {
return b - a;
})
var sortedMemberArr = memberArr.map(function(member, index) {
return member+''+(index+1);
})
updatedObj[key] = sortedMemberArr;
})
console.log(updatedObj)
/*
// Code Using Modern Syntax
const data = Object.entries(obj).map(([key, value]) => {
const memberCount = value.reduce((store, val) => {
const key = val.replace(/[0-9]/g, '');
const number = +val.replace(/^\D+/g, '');
if(!['SELF', 'SPOUSE'].includes(key)) {
store[key] = Array(number).fill(key);
} else {
store[key] = [key];
}
return store;
}, {})
const sortedMembers = Object.values(memberCount).flat().sort((a, b) => b - a);
const sortedMembersWithNumber = sortedMembers.map((member, index) => `${member}${index+1}`)
return [key, sortedMembersWithNumber]
})
const updatedObj = Object.fromEntries(data);
console.log(updatedObj)
*/

Sorting Object of Objects using Object.values()

I'm trying to sort an object of objects using Object.values(), sort() and map() into pages based on a nested value. It's works perfect in FF, but Chrome for some reason returns pages full of unsorted items.
Example structure of list:
{
1: {
id: 1,
rank: 10,
name: "foo"
},
2: {
id: 2,
rank: 24,
name: "bar"
},
3: {
id: 3,
rank: 11,
name: "baz"
},
...
}
Example:
const out = document.getElementById("out");
const sortBy = "rank";
const perPage = 10;
let list = {};
for (let i = 1; i < 50; i++) {
list[i] = {
id: i,
rank: rand(10, 200),
name: generateName(rand(6, 12))
}
}
const values = Object.values(list);
const pages = values.sort((a, b) => {
if (sortBy === "rank") {
return a.rank < b.rank;
} else if (sortBy === "name") {
return a.name > b.name;
}
})
.map((item, i) => {
return i % perPage === 0 ? values.slice(i, i + perPage) : null;
}).filter(page => page);
for (const page of pages) {
for (const item of page) {
out.value += `${item.rank}\n`;
}
}
// HELPERS
function rand(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1) + min);
}
function generateName(length) {
let result = '';
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
const charactersLength = characters.length;
for (var i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
}
<textarea name="" id="out" cols="60" rows="30"></textarea>
Demo Pen
Thank you #Pointy. So the answer should be like this.
const pages = values.sort((a, b) => a.rank < b.rank ? 1 : -1)

How can i remove the duplicated elements in my array?

I used this code but with that i can only remove the next one and the previuous one if its equal
for (let i = 0; i < this.userlist.length; i++) {
if (this.userlist[i] == this.userlist[i+1])
this.userlist.splice(i+1, 1);
if (this.userlist[i-1] == this.userlist[i+1])
this.userlist.splice(i+1, 1);
}
How can i remove all the duplicated elements?
edit n°1
data() {
return {
formlogin: "",
userID: "Guest",
logged: false,
userlist: []
};
},
mounted() {
this.userID = localStorage.getItem("userID");
if (this.userID != "Guest") this.logged = localStorage.getItem("logged");
if (localStorage.userlist)
this.userlist = JSON.parse(localStorage.getItem("userlist"));
},
props: {},
methods: {
login: function() {
if (this.formlogin != "") {
this.userID = this.formlogin;
this.formlogin = "";
this.logged = true;
localStorage.setItem("logged", this.logged);
localStorage.setItem("userID", this.userID);
this.userlist.push(this.userID);
for (let i = 0; i < this.userlist.length; i++) {
if (this.userlist[i] == this.userlist[i + 1])
this.userlist.splice(i + 1, 1);
if (this.userlist[i - 1] == this.userlist[i + 1])
this.userlist.splice(i + 1, 1);
}
localStorage.setItem("userlist", JSON.stringify(this.userlist));
console.log("data sent :", this.userID, this.logged);
alert("Welcome : " + this.userID);
} else alert("cant login with a null username");
},
thats how my userlist will be updated.
es6 spread operator with Set()
var items = [4,5,4,6,3,4,5,2,23,1,4,4,4];
console.log([...new Set(items)]);
OR,
var items = [4,5,4,6,3,4,5,2,23,1,4,4,4];
console.log(Array.from(new Set(items)));
Using filter method
var items = [4,5,4,6,3,4,5,2,23,1,4,4,4];
var newItems = items.filter((item, i) => items.indexOf(item) === i);
console.log(newItems);
Using reduce method
var items = [4,5,4,6,3,4,5,2,23,1,4,4,4];
var newItems = items.reduce((uniq, item) => uniq.includes(item) ? uniq: [...uniq, item], []);
console.log(newItems);
You almost got it!
for (let i = 0; i < this.userlist.length; i++) {
if (this.userlist[i] == this.userlist[i+1]){
this.userlist.splice(i+1, 1);
i--;
}
}
In your solution, two elements are removed at most. What you can do instead is remove the next element an make sure that the index doesn't increase (hence the i--, so in the next iteration if will check the same index again).
This however only works for sorted lists. Check solanki's answer for a more generic one.
Using reduce you can do something like this. Check if the current index is same as the first index found in data
var data = ["user", "user", "user", "foo", "foo"]
var res = data.reduce((acc, elem, idx, arr)=> (arr.indexOf(elem) === idx ? [...acc, elem] : acc),[]);
console.log(res)

I'm having trouble displaying my randomly selected object

I am having trouble displaying the random object and the properties of that random object. The goal of this project is to have a list of stockItems, and when I press a button, it selects a determined number of those objects and displays them in an HTML p tag. Right now when I try to display it, it prints out as [object]. The goal is to have the properties of the selected object on different lines.
Here is the code I am working with:
function buildShopItems(count) {
var shopItems = [], i, itemIndex;
count = stockItems.length < count ? stockItems.length : count;
function getUniqueRandomItem() { //from stock
var item;
while (true) {
item = stockItems[Math.floor(Math.random() * stockItems.length)];
if (shopItems.indexOf(item) < 0) return item;
}
}
for (i = 0; i < count; i++) {
shopItems.push(getUniqueRandomItem());
}
return shopItems;
console.log(shopItems);
}
var stockItems = [
{ item: "sword", type: "weapon", weight: "5 lbs.", cost: "10 gold" },
{ item: "hammer", type: "weapon", weight: "8 lbs.", cost: "7 gold" }
//...
];
var shopItems = buildShopItems(1);
console.log(shopItems);
document.getElementById("item").innerHTML = shopItems.item;
document.getElementById("type").innerHTML = shopItems.type;
document.getElementById("weight").innerHTML = shopItems.weight;
document.getElementById("cost").innerHTML = shopItems.cost;
The problem was with your usage of indexOf. You can use indexOf to search for an object because in javascript you can't compare object using == or === and indexOf uses ===. Also made some syntax updates for you.
'use strict'
const stockItems = [
{ item: "sword", type: "weapon", weight: "5 lbs.", cost: "10 gold" },
{ item: "hammer", type: "weapon", weight: "8 lbs.", cost: "7 gold" }
];
function isEquivalent(a, b) {
// Create arrays of property names
const aProps = Object.getOwnPropertyNames(a);
const bProps = Object.getOwnPropertyNames(b);
// If number of properties is different,
// objects are not equivalent
if (aProps.length != bProps.length) {
return false;
}
for (let i = 0; i < aProps.length; i++) {
const propName = aProps[i];
// If values of same property are not equal,
// objects are not equivalent
if (a[propName] !== b[propName]) {
return false;
}
}
// If we made it this far, objects
// are considered equivalent
return true;
}
// normal indexof will not work with object because it uses strict equality
function myIndexOf(array, object) {
for (let i = 0; i < array.length; i++) {
if (isEquivalent(array[i], object)) return i;
}
return -1;
}
function getUniqueRandomItem(shopItems) { //from stock
var item;
while (true) {
item = stockItems[Math.floor(Math.random() * stockItems.length)];
if (myIndexOf(shopItems, item) < 0) return item;
}
}
function buildShopItems(count) {
count = stockItems.length < count ? stockItems.length : count;
const shopItems = [];
for (let i = 0; i < count; i++) {
const item = getUniqueRandomItem(shopItems);
shopItems.push(item);
}
return shopItems;
}
const shopItems = buildShopItems(1);
console.log(shopItems);

Categories