how to collate an array of dictionaries into a dictionary of arrays? - javascript

I have this data:
list = [
{name:'apple', category: "fruit", price: 1.22 },
{name:'pear', category: "fruit", price: 2.22 },
{name:'coke', category: "drink", price: 3.33 },
{name:'sprite', category: "drink", price: .44 },
];
And I'd like to create a dictionary keyed on category, whose value is an array that contains all the products of that category. My attempt to do this failed:
var tmp = {};
list.forEach(function(product) {
var idx = product.category ;
push tmp[idx], product;
});
tmp;

function dictionary(list) {
var map = {};
for (var i = 0; i < list.length; ++i) {
var category = list[i].category;
if (!map[category])
map[category] = [];
map[category].push(list[i].name); // add product names only
// map[category].push(list[i]); // add complete products
}
return map;
}
var d = dictionary(list); // call
You can test it on jsfiddle.

Related

Javascript: How would you get the highest rated product category based on these 2 arrays

Assuming I have 2 arrays:
const products = [
{
name: 'prod1',
category: 'Meat'
},
{
name: 'prod2',
category: 'Meat'
},
{
name: 'prod3',
category: 'Dairy'
}];
const rate = [
{
name: 'prod1',
rate: 23,
},
{
name: 'prod2',
rate: 36
},
{
name: 'prod3',
rate: 50,
}];
How would you get the category that has the highest sum rate? For example, prod1 and prod2 share the same category 'Meat" and hence the rate for meat is 36 + 23 = 59.
The way I thought about it is to create an adjusted Array of products where each entry will contain the rate from the second array and then I will create a result array and push an object of category and sumRate after iterating the adjustedArray.
So if the result Array has an object with category, I would adjust the sum and add the new rate, if not I'll create a new entry with category: rate.
Can we do this in a very optimal way?
As the OP probably knows, canonical grouping goes like this...
const prodsByCategory = products.reduce((acc, p) => {
let cat = p.category;
if (!acc[cat]) acc[cat] = [];
acc[cat].push(p);
return acc;
}, {});
Modify this a little to add the data which will be needed to optimize.
const prodsByCategory = products.reduce((acc, p) => {
let cat = p.category;
// instead of just an array, keep an array and a total
if (!acc[cat]) acc[cat] = { products: [], totalRate: 0 };
// instead of just pushing, push and increment total with a lookup
acc[cat].products.push(p);
acc[cat].totalRate += rateForProduct(p) || 0;
return acc;
}, {});
We need a lookup for rateForProduct, like this:
const rateForProduct = product => {
return rate.find(r => r.name === product.name)?.rate || 0;
}
That should produce an object keyed by category with values that have a prop called totalRate. Sort those entries so that the first one is maximized. Here's a demo...
const products = [{
name: 'prod1',
category: 'Meat'
},
{
name: 'prod2',
category: 'Meat'
},
{
name: 'prod3',
category: 'Dairy'
}
];
const rate = [{
name: 'prod1',
rate: 23,
},
{
name: 'prod2',
rate: 36
},
{
name: 'prod3',
rate: 50,
}
];
const rateForProduct = product => {
return rate.find(r => r.name === product.name)?.rate || 0;
}
const prodsByCategory = products.reduce((acc, p) => {
let cat = p.category;
if (!acc[cat]) acc[cat] = {
products: [],
totalRate: 0
};
acc[cat].products.push(p);
acc[cat].totalRate += rateForProduct(p);
return acc;
}, {});
const sortedEntries = Object.entries(prodsByCategory).sort((a, b) => b[1].totalRate - a[1].totalRate);
const bestEntry = {
category: sortedEntries[0][0],
rate: sortedEntries[0][1].totalRate
}
console.log(bestEntry);

How to convert an unorganized array into an grouped array by id

I'm trying to create an array that contains objects with an id and amount, grouped by id. The ids needs to be unique. So if there is 2 objects with same id, the amount will be added.
I can do it with nested for-loops, but I find this solution inelegant and huge. Is there a more efficient or cleaner way of doing it?
var bigArray = [];
// big Array has is the source, it has all the objects
// let's give it 4 sample objects
var object1 = {
id: 1,
amount: 50
}
var object2 = {
id: 2,
amount: 50
}
var object3 = {
id: 1,
amount: 150
}
var object4 = {
id: 2,
amount:100
}
bigArray.push(object1,object2,object3,object4);
// organizedArray is the array that has unique ids with added sum. this is what I'm trying to get
var organizedArray = [];
organizedArray.push(object1);
for(var i = 1; i < bigArray.length; i++ ) {
// a boolean to keep track whether the object was added
var added = false;
for (var j = 0; j < organizedArray.length; j++){
if (organizedArray[j].id === bigArray[i].id) {
organizedArray[j].amount += bigArray[i].amount;
added = true;
}
}
if (!added){
// it has object with new id, push it to the array
organizedArray.push(bigArray[i]);
}
}
console.log(organizedArray);
You can definitly make it cleaner and shorter by using reduce, not sure about efficiency though, i would say a traditional for loop is more efficient :
var bigArray = [];
var object1 = {id: 1, amount: 50}
var object2 = {id: 2, amount: 50}
var object3 = {id: 1, amount: 150}
var object4 = {id: 2, amount: 100}
bigArray.push(object1, object2, object3, object4);
var organizedArray = bigArray.reduce((acc, curr) => {
// check if the object is in the accumulator
const ndx = acc.findIndex(e => e.id === curr.id);
if(ndx > -1) // add the amount if it exists
acc[ndx].amount += curr.amount;
else // push the object to the array if doesn't
acc.push(curr);
return acc;
}, []);
console.log(organizedArray)
Rather than an organized array, how about a single object whose keys are the ids and values are the sums.
var bigArray = [
{ id: 1, amount: 50 },
{ id: 2, amount: 50 },
{ id: 1, amount: 150 },
{ id: 2, amount: 100 }
];
let total = {}
bigArray.forEach(obj => {
total[obj.id] = (total[obj.id] || 0) + obj.amount;
});
console.log(total);
If you really need to convert this to an array of objects then you can map the keys to objects of your choosing like this:
var bigArray = [
{ id: 1, amount: 50 },
{ id: 2, amount: 50 },
{ id: 1, amount: 150 },
{ id: 2, amount: 100 }
];
let total = {}
bigArray.forEach(obj => {
total[obj.id] = (total[obj.id] || 0) + obj.amount;
});
console.log(total);
// If you need the organized array:
let organizedArray = Object.keys(total).map(key => ({ id: key, amount: total[key] }));
console.log(organizedArray);
function getUniqueSums(array) {
const uniqueElements = [];
const arrayLength = array.length;
for(let index = 0; index < arrayLength; index++) {
const element = array[index];
const id = element.id;
const uniqueElement = findElementByPropertyValue(uniqueElements, 'id', id);
if (uniqueElement !== null) {
uniqueElement.amount += element.amount;
continue;
}
uniqueElements.push(element);
}
return uniqueElements;
}
function findElementByPropertyValue(array, property, expectedValue) {
const arrayLength = array.length;
for(let index = 0; index < arrayLength; index++) {
const element = array[index];
const value = element[property];
if (value !== expectedValue) {
continue;
}
return element;
}
return null;
}
This is an untested code. You will be able to understand the logic. Logic is almost same yours. But, perhaps a more readable code.

how to count duplicate values object to be a value of object

how to count the value of object in new object values
lets say that i have json like this :
let data = [{
no: 3,
name: 'drink'
},
{
no: 90,
name: 'eat'
},
{
no: 20,
name: 'swim'
}
];
if i have the user pick no in arrays : [3,3,3,3,3,3,3,3,3,3,3,90,20,20,20,20]
so the output should be an array
[
{
num: 3,
total: 11
},
{
num: 90,
total: 1
},
{
num:20,
total: 4
}
];
I would like to know how to do this with a for/of loop
Here is the code I've attempted:
let obj = [];
for (i of arr){
for (j of data){
let innerObj={};
innerObj.num = i
obj.push(innerObj)
}
}
const data = [{"no":3,"name":"drink"},{"no":90,"name":"eat"},{"no":20,"name":"swim"}];
const arr = [3,3,3,3,3,3,3,3,3,3,3,20,20,20,20,80,80];
const lookup = {};
// Loop over the duplicate array and create an
// object that contains the totals
for (let el of arr) {
// If the key doesn't exist set it to zero,
// otherwise add 1 to it
lookup[el] = (lookup[el] || 0) + 1;
}
const out = [];
// Then loop over the data updating the objects
// with the totals found in the lookup object
for (let obj of data) {
lookup[obj.no] && out.push({
no: obj.no,
total: lookup[obj.no]
});
}
document.querySelector('#lookup').textContent = JSON.stringify(lookup, null, 2);
document.querySelector('#out').textContent = JSON.stringify(out, null, 2);
<h3>Lookup output</h3>
<pre id="lookup"></pre>
<h3>Main output</h3>
<pre id="out"></pre>
Perhaps something like this? You can map the existing data array and attach filtered array counts to each array object.
let data = [
{
no: 3,
name: 'drink'
},
{
no:90,
name: 'eat'
},
{
no:20,
name: 'swim'
}
]
const test = [3,3,3,3,3,3,3,3,3,3,3,90,20,20,20,20]
const result = data.map((item) => {
return {
num: item.no,
total: test.filter(i => i === item.no).length // filters number array and then checks length
}
})
You can check next approach using a single for/of loop. But first I have to create a Set with valid ids, so I can discard noise data from the test array:
const data = [
{no: 3, name: 'drink'},
{no: 90, name: 'eat'},
{no: 20, name: 'swim'}
];
const userArr = [3,3,3,3,3,3,3,3,7,7,9,9,3,3,3,90,20,20,20,20];
let ids = new Set(data.map(x => x.no));
let newArr = [];
for (i of userArr)
{
let found = newArr.findIndex(x => x.num === i)
if (found >= 0)
newArr[found].total += 1;
else
ids.has(i) && newArr.push({num: i, total: 1});
}
console.log(newArr);

Manipulate a key value pair to a string in Javascript

I need some help in manipulating a value pair array to return a string in a specific layout
This is the string i am trying to achieve:
'&test=id:1111|na:shoes|ca:shoe&test=id:2222|na:top|ca:tops'
This is the array I am trying to manipulate into my string
var prodlist =
[
{
name: 'shoe',
sku: '1111',
category: 'shoes'
},
{
name: 'top',
sku: '2222',
category: 'tops'
}
]
Here is what I have tried.
I added the 'deleteme' into the array thinking i could to a substitute later down the script.
function(){
var prods = prodlist;
var array = [];
for (var i = 0; i < prods.length; i++) {
var sku = (prods[i]['sku']);
var name = (prods[i]['name']);
var cat = (prods[i]['category']);
array.push({
deleteme: &test=id,
id: sku,
na: name,
ca: cat,
});
}
var newarray = array.toString();
return newarray;
}
At the moment this function returns '[object Object],[object Object]'
any help would be much appreciated.
Thanks
Quick and easy
function prods() {
var prods = prodlist;
var array = [];
for (let product of prods)
array.push('test=id:' + product.sku+ '|na:' + product.name + '|ca:' + product.category);
return '&' + array.join('&');
}
how about something like this?
var prodlist =
[{name: 'shoe',
sku: '1111',
category: 'shoes'},
{name: 'top',
sku: '2222',
category: 'tops'}]
var strTemplate = "&test=id:%sku%|na:%name%|ca:%category%"
prodlist.map( function(obj){
return strTemplate.replace(/%(\w+)%/g, function($1, $2) {
return obj[$2]
})
}).join('')
//returns
// "&test=id:1111|na:shoe|ca:shoes&test=id:2222|na:top|ca:tops"
or ES6 version (edited it down further as suggested by nnnnnn )
prodlist.map( obj => strTemplate.replace(/%(\w+)%/g, ($1, $2) => obj[$2])).join('')

Group json object in javascript

I want to group json array by first letter
This is my data records it quesry from sqlitedb
Ex :
[
{"pid":2,"ID":1,"title":"aasas as"},
{"pid":3,"ID":2,"title":"family"},
{"pid":4,"ID":3,"title":"fat111"}
]
I need this output
{
A: [{
title: "aasas as",
ID: 1
}],
F: [{
title: "family",
ID: 2
}, {
title: "fat111",
ID: 3
}]
}
Try this
var data = [
{"pid":2,"ID":1,"title":"aasas as"},
{"pid":3,"ID":2,"title":"family"},
{"pid":4,"ID":3,"title":"fat111"}
];
var result = {},
i,
len = data.length,
key;
for (i = 0; i < len; i++) {
key = data[i].title.substring(0, 1); // get first word from string
if (!result[key]) { // if key does not exists in result, create it
result[key] = [];
}
result[key].push(data[i]); // else push data
}
console.log(result);

Categories