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
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);
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 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);
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('')
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);