I nave an array:
const arr = [
{ name: "aa", type: "total", count: 28394 },
{ name: "aa", type: "featured", count: 4 },
{ name: "aa", type: "noAnswers", count: 5816 },
{ name: "ba", type: "total", count: 148902 },
{ name: "ba", type: "featured", count: 13 },
{ name: "ba", type: "noAnswers", count: 32527 },
{ name: "cc", type: "total", count: 120531 },
{ name: "cc", type: "featured", count: 6 },
{ name: "cc", type: "noAnswers", count: 24170 }
];
const arrResult = [
{ name: "aa", total: 28394, featured: 4, noAnswers: 5816 },
{ name: "ba", total: 148902, featured: 13, noAnswers: 32527 },
{ name: "cc", total: 120531, featured: 6, noAnswers: 24170 }
];
I come up with this code:
let output = [];
const unique = [...new Set(arr.map(item => item.name))];
for(const key of unique) {
let result = arr.filter(x => {
return x.name === key;
});
output.push({
name: key,
// need to get the rest of the properties here
// total
// featured
// noAnswers
});
}
The only one thing I can not figure out is how to get the property names.
Any ideas?
You can try something like this:
Idea:
Create a hashMap so you can group objects via name.
Then, add necessary properties to this group.
Finally, loop over keys and create final object with name property added back.
const arr = [ { name: "aa", type: "total", count: 28394 }, { name: "aa", type: "featured", count: 4 }, { name: "aa", type: "noAnswers", count: 5816 }, { name: "ba", type: "total", count: 148902 }, { name: "ba", type: "featured", count: 13 }, { name: "ba", type: "noAnswers", count: 32527 }, { name: "cc", type: "total", count: 120531 }, { name: "cc", type: "featured", count: 6 }, { name: "cc", type: "noAnswers", count: 24170 } ];
const hashMap = arr.reduce((acc, item) => {
acc[item.name] = acc[item.name] || {};
acc[item.name][item.type] = item.count;
return acc;
}, {});
const result = Object.keys(hashMap).map((name) => Object.assign({}, {name}, hashMap[name] ));
console.log(result)
Working:
What I'm doing is I'm creating a new object for every new name. So, this: acc[item.name] = acc[item.name] || {}; checks if the entry is unavailable or not.
If unavailable, return a new object.
If available, return same object's reference.
So for any given name, you will only refer to same object.
Now this: acc[item.name][item.type] = item.count sets the properties. As we are referring to same object, you are setting property in one place. So if you have duplicate entries, say
[
{ name: "aa", type: "total", count: 28394 },
{ name: "aa", type: "total", count: 123},
]
output will have total: 123 instead.
So, at the end, you have a structure like:
{
aa: {
total: <something>,
feature: <something>,
...
}
}
Now all you have to do is merge the name in this object and return the value. You can also create the object with name property as default (as done by adiga). Thats something I didn't think while answering. So crediting instead of answering.
You can use reduce and destructuring like this:
The idea is to create an object with key as the name property and value as the final objects you need in the output. So, that you can simply use Object.values to get the final array:
const arr=[{name:"aa",type:"total",count:28394},{name:"aa",type:"featured",count:4},{name:"aa",type:"noAnswers",count:5816},{name:"ba",type:"total",count:148902},{name:"ba",type:"featured",count:13},{name:"ba",type:"noAnswers",count:32527},{name:"cc",type:"total",count:120531},{name:"cc",type:"featured",count:6},{name:"cc",type:"noAnswers",count:24170}];
const merged = arr.reduce((acc,{name,type,count}) =>
((acc[name] = acc[name] || {name})[type] = count, acc)
,{})
console.log(Object.values(merged))
This is equivalent to :
const arr=[{name:"aa",type:"total",count:28394},{name:"aa",type:"featured",count:4},{name:"aa",type:"noAnswers",count:5816},{name:"ba",type:"total",count:148902},{name:"ba",type:"featured",count:13},{name:"ba",type:"noAnswers",count:32527},{name:"cc",type:"total",count:120531},{name:"cc",type:"featured",count:6},{name:"cc",type:"noAnswers",count:24170}];
/* Our goal is to create a merged object like this:
{
"aa": {
"name": "aa",
"total": 28394,
"featured": 4,
"noAnswers": 5816
},
"ba": {
"name": "ba",
"total": 148902,
....
},
"cc": {
"name": "cc",
......
}
}
The advantage of using object accumulator is we can access it like this: acc[name]
*/
const merged = arr.reduce((acc, {name,type,count} /*Destructuring*/) => {
/* if the accumulator doesn't have the current "name" key,
create new object
else use the existing one;
{name} is same as {name: name}
*/
acc[name] = acc[name] || {name};
/* To the inner object,
add a key with the "type" value and assign it to "count" value
*/
acc[name][type] = count;
// return the accumulator
return acc;
}, {})
// use Object.values to get the value part of the merged obejct into an array
console.log(Object.values(merged))
var op = {name : key};
for(i=0; i < result.length; i++){
op[result[i].type] = result[i].count;
}
output.push(op);
just adding this will work fine. However your code is not the most efficient.
Hashing based on name will make it faster
const arr = [
{ name: "aa", type: "total", count: 28394 },
{ name: "aa", type: "featured", count: 4 },
{ name: "aa", type: "noAnswers", count: 5816 },
{ name: "ba", type: "total", count: 148902 },
{ name: "ba", type: "featured", count: 13 },
{ name: "ba", type: "noAnswers", count: 32527 },
{ name: "cc", type: "total", count: 120531 },
{ name: "cc", type: "featured", count: 6 },
{ name: "cc", type: "noAnswers", count: 24170 }
];
let output = [];
const unique = [...new Set(arr.map(item => item.name))];
for(const key of unique) {
let result = arr.filter(x => {
return x.name === key;
});
var op = {name : key};
for(i=0; i < result.length; i++){
op[result[i].type] = result[i].count;
}
output.push(op);
}
console.log(output);
The following is the most efficient way of doing it :
const arr = [
{ name: "aa", type: "total", count: 28394 },
{ name: "aa", type: "featured", count: 4 },
{ name: "aa", type: "noAnswers", count: 5816 },
{ name: "ba", type: "total", count: 148902 },
{ name: "ba", type: "featured", count: 13 },
{ name: "ba", type: "noAnswers", count: 32527 },
{ name: "cc", type: "total", count: 120531 },
{ name: "cc", type: "featured", count: 6 },
{ name: "cc", type: "noAnswers", count: 24170 }
];
var hash = {};
var result = [];
for(var i=0; i < arr.length; i++){
if(!arr[i].name in hash)
hash[arr[i].name] = {}
let temp = {};
temp[arr[i].type] = arr[i].count;
hash[arr[i].name] = Object.assign(temp, hash[arr[i].name]);
}
for(var key in hash)
result.push({name : key, ...hash[key]})
console.log(result)
You can use find operator of javascript to grab the desired row from arrResult Change your code like below-
for(const key of unique) {
let result = arr.filter(x => {
return x.name === key;
});
var currResult = arrResult.find(x => x.name == key);
output.push({
name: key,
// need to get the rest of the properties here
total: currResult.total,
featured: currResult.featured,
noAnswers: currResult.noAnswers
});
}
JSFiddle: https://jsfiddle.net/ashhaq12345/z8royg5w/
const arr = [
{ name: "aa", type: "total", count: 28394 },
{ name: "aa", type: "featured", count: 4 },
{ name: "aa", type: "noAnswers", count: 5816 },
{ name: "ba", type: "total", count: 148902 },
{ name: "ba", type: "featured", count: 13 },
{ name: "ba", type: "noAnswers", count: 32527 },
{ name: "cc", type: "total", count: 120531 },
{ name: "cc", type: "featured", count: 6 },
{ name: "cc", type: "noAnswers", count: 24170 }
];
const names = [...new Set(arr.map(item => item.name))]
const output = {};
names.forEach(name => {output[name] = {}});
arr.forEach(item => {
output[item.name][item.type] = item.count
});
const result = Object.entries(output).map(([name, rest]) => ({name, ...rest}))
console.log(result);
const arrResult = [
{ name: "aa", total: 28394, featured: 4, noAnswers: 5816 },
{ name: "ba", total: 148902, featured: 13, noAnswers: 32527 },
{ name: "cc", total: 120531, featured: 6, noAnswers: 24170 }
];
You can simply use for loop to iterate over your array and take a temp array and take map and fill the map using required data and then push your map into temp array like following.
const arr = [
{ name: "aa", type: "total", count: 28394 },
{ name: "aa", type: "featured", count: 4 },
{ name: "aa", type: "noAnswers", count: 5816 },
{ name: "ba", type: "total", count: 148902 },
{ name: "ba", type: "featured", count: 13 },
{ name: "ba", type: "noAnswers", count: 32527 },
{ name: "cc", type: "total", count: 120531 },
{ name: "cc", type: "featured", count: 6 },
{ name: "cc", type: "noAnswers", count: 24170 }
];
let result = [];
for( var i = 0; i < arr.length; i++)
{
let data = {};
if( arr[i].type == 'total')
{
data.name = arr[i].name;
data.total = arr[i].count;
data.featured = arr[i+1].count;
data.noAnswers = arr[i+2].count;
result.push(data);
}
}
console.log(result);
Related
I want the result to be summing all the qty of same cat.
var data = [
{ cat: 'EK-1',name:"test",info:"mat", quantity: 3},
{ cat: 'EK-2', name:"test2",info:"nat"quantity: 1}
];
I tried like this below i have array of object having some similar objects. how to add qty and create unque objects .below i have given what i tried.
var data = [{
cat: 'EK-1',
name: "test",
info: "mat",
quantity: 1
},
{
cat: 'EK-1',
name: "test",
info: "mat",
quantity: 1
},
{
cat: 'EK-1',
name: "test",
info: "mat",
quantity: 1
},
{
cat: 'EK-2',
name: "test2",
info: "nat",
quantity: 1
}
];
const products = Array.from(data.reduce((acc, {
cat,
quantity
}) =>
acc.set(cat, (acc.get(cat) || 0) + quantity),
new Map()
), ([cat, quantity]) => ({
cat,
quantity
}));
console.log(products);
You can do this using Array#reduce, using the accumulator to pass on the new object:
var data = [ { cat: "EK-1", name: "test", info: "mat", quantity: 1, }, { cat: "EK-1", name: "test", info: "mat", quantity: 1, }, { cat: "EK-1", name: "test", info: "mat", quantity: 1, }, { cat: "EK-2", name: "test2", info: "nat", quantity: 1, }, ];
let seen = [];
const res = data.reduce((acc, { cat, ...rest }) => {
const idx = seen.indexOf(cat);
if (idx == -1) (acc.push({cat, ...rest}), seen.push(cat));
else acc[idx].quantity++;
return acc;
}, []);
console.log(res);
I have an array of objects. In each object there is a details property (array) with more than one object - for this example I am only showing one.
I am looking for the rating property in each details and looking to find minimum value (in this example 3.1) ... is there a simpler/cleaner way of achieving this?
const ratings = [{ id: 'ABC', details: [{ type: 'VALUE', rating: 9.5 }] }, { id: 'DEF', details: [{ type: 'VALUE', rating: 3.1 }] }, { id: 'GHI', details: [{ type: 'VALUE', rating: 4.5 }] }]
const ids = ['ABC', 'DEF', 'GHI']
const array = []
ids.forEach(element => {
const valueScore = ratings?.find(r => r.id === element)?.details?.find(c => c.type === 'VALUE')
if (valueScore.rating) {
array.push(valueScore.rating)
}
})
console.log('MIN VALUE', Math.min(...array))
You can use flatMap and then apply math.min to get minimum value:
const ratings = [{ id: 'ABC', details: [{ type: 'VALUE', rating: 9.5 }] }, { id: 'DEF', details: [{ type: 'VALUE', rating: 3.1 },{ type: 'VALUE1', rating: 2.1 }] }, { id: 'GHI', details: [{ type: 'VALUE', rating: 4.5 }] }];
const ids = ['ABC', 'DEF', 'GHI']
console.log(Math.min(...ratings.flatMap(o=>
ids.includes(o.id) ? o.details.flatMap(p=>
p.type=='VALUE' ? p.rating : []) : [])));
You can do something like this:
Why this method is preferred is, you are running the loop perfectly over each ratings item and also through each details array item.
ratings = [
{ id: "ABC", details: [{ type: "VALUE", rating: 9.5 }] },
{
id: "DEF",
details: [{ type: "VALUE", rating: 3.1 }, { type: "VALUE1", rating: 2.1 }]
},
{ id: "GHI", details: [{ type: "VALUE", rating: 4.5 }] }
];
ids = ["ABC", "DEF", "GHI"];
const detailsRatingArr = [];
if (ratings.length === ids.length) {
ratings.forEach(eachRating => {
ids.forEach(eachId => {
if (eachRating.id === eachId) {
eachRating.details.forEach(eachDetail => {
detailsRatingArr.push(eachDetail.rating);
});
}
});
});
}
console.log("DETAILS ARRAY ==>", detailsRatingArr);
detailsRatingArr.sort((a, b) => a - b);
console.log("Minimum Rating ==>", detailsRatingArr[0]);
I have two arrays of objects as shown below :
categories = [
{ name: "performance", id: 1 },
{ name: "understanding", id: 2 }
]
queries = [
{ name: "A", categoryId: "1" },
{ name: "B", categoryId: "1" },
{ name: "C", categoryId: "1" },
{ name: "D", categoryId: "2" }
]
Now, using these two arrays of objects, I need following array as a result:
process = [
{ category: "performance", query: [
{ name: "A" },
{ name: "B" },
{ name: "C" }
]},
{ category: "understanding", query: [{ name: "D" }] }
]
I have to match the categoryId with process's id and then create the above array.
I have tried the following way to solve this but could not get desired result.
const test = [];
categories.forEach((element) => {
const r = que.filter((res) => res.categoryId === element.id);
queries.forEach((rt) => {
if (rt.categoryId === element.id) {
test.push({
category: element.name,
query: [{
name: rt.name,
}],
});
}
});
});
Is this possible using any built in array methods in JavaScript?
Thanks in advance
Using Array.reduce, you can group queries by categoryId.
Based on that groupedBy object, using Array.map, you can get the result you want.
const categories = [{
name: "performance",
id: "1"
},{
name: "understanding",
id: "2"
}];
const queries = [{
name: "A",
categoryId: "1"
}, {
name: "B",
categoryId: "1"
}, {
name: "C",
categoryId: "1"
}, {
name: "D",
categoryId: "2"
}];
const groupByQueries = queries.reduce((acc, cur) => {
acc[cur.categoryId] ?
acc[cur.categoryId].push({ name: cur.name })
: acc[cur.categoryId] = [ { name: cur.name } ];
return acc;
}, {});
const result = categories.map(({ name, id }) => ({
category: name,
query: groupByQueries[id]
}));
console.log(result);
categories = [{name: "performance", id: 1},{name: "understanding", id: 2}];
queries = [{name: "A", categoryId: "1"}, {name: "B", categoryId: "1"}, {name: "C", categoryId: "1"}, {name: "D", categoryId: "2"}]
const test = [];
categories.forEach((element) => {
let temp = []
queries.map(item =>{
if(element.id.toString() === item.categoryId)
temp.push({name: item.name})
})
test.push({category:element.name,query:temp})
});
console.log(test)
I have the following data:
const data = [
{
parent: {
id: "1",
name: "Europe"
},
item: {
name: "Italy"
},
score: 5
},
{
parent: {
id: "1",
name: "Europe"
},
item: {
name: "France"
},
score: 4.5
},
{
parent: {
id: "1",
name: "Europe"
},
item: {
name: "UK"
},
score: 4.9
},
{
parent: {
id: "2",
name: "Afrique"
},
item: {
name: "Morocco"
},
score: 3.1
},
{
parent: {
id: "2",
name: "Afrique"
},
item: {
name: "Egypt"
},
score: 3.9
}
];
I want to group it based on the parent.id and calculate the average score, so I can have the following result:
[
{
parent: {
id: "1",
name: "Europe",
items: [
{
name: "Italy"
},
{
name: "France"
},
{
name: "UK"
}
],
score: 4.8
}
},
{
parent: {
id: "2",
name: "Afrique",
items: [
{
name: "Morocco"
},
{
name: "Egypt"
}
],
score: 3.5
}
}
]
I used the following function, but it doesn't work for the nested key, and also it's doesn't return the desired result schema.
let group = cars.reduce((r, a) => {
console.log("a", a);
console.log('r', r);
r[a.make] = [...r[a.parent.id] || [], a];
return r;
}, {});
console.log("group", group);
You can use _reduce() function: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce
var result = data.reduce((res, data) => {
if(!res[data.parent.id]) {
data.item = [data.item];
res[data.parent.id] = data;
} else {
res[data.parent.id]['item'].push(data['item']);
res[data.parent.id]['score'] = (res[data.parent.id]['score'] + data['score'])/2;
}
return res;
}, [])
.filter(x => x != null)
const data = [
{
parent: {
id: "1",
name: "Europe"
},
item: {
name: "Italy"
},
score: 5
},
{
parent: {
id: "1",
name: "Europe"
},
item: {
name: "France"
},
score: 4.5
},
{
parent: {
id: "1",
name: "Europe"
},
item: {
name: "UK"
},
score: 4.9
},
{
parent: {
id: "2",
name: "Afrique"
},
item: {
name: "Morocco"
},
score: 3.1
},
{
parent: {
id: "2",
name: "Afrique"
},
item: {
name: "Egypt"
},
score: 3.9
}
];
var result = data.reduce((res, data) => {
if(!res[data.parent.id]) {
data.item = [data.item];
res[data.parent.id] = data;
} else {
res[data.parent.id]['item'].push(data['item']);
res[data.parent.id]['score'] = (res[data.parent.id]['score'] + data['score'])/2;
}
return res;
}, [])
.filter(x => x != null)
console.log(result)
Create an object/hashmap, then format the resulting object into an array.
let continents = {}
data.forEach(function(country){
const continent_id = country.parent.id
let continent = continents[continent_id]
if(!continent){
continent = {
id: continent_id,
name: country.parent.name,
items: [],
}
continents[continent_id] = continent
}
continent.items.push({
name: country.item.name,
score: country.score
})
})
continents = Object.entries(continents).map(item => ({parent: item[1]}))
console.log(continents)
Output:
[
{
"parent":{
"id":"1",
"name":"Europe",
"items":[
{
"name":"Italy",
"score":5
},
{
"name":"France",
"score":4.5
},
{
"name":"UK",
"score":4.9
}
]
}
},
{
"parent":{
"id":"2",
"name":"Afrique",
"items":[
{
"name":"Morocco",
"score":3.1
},
{
"name":"Egypt",
"score":3.9
}
]
}
}
]
From the data you've provided if you additionaly need to count average of score property, use the following reduce method: it will iterate trough your data, group it and calculate total score value and count of score values. And after reduce groups object perform map that will calculate average for score for all the groups using totalScore and scoreCount
const data = [
{
parent: {
id: "1",
name: "Europe"
},
item: {
name: "Italy"
},
score: 5
},
{
parent: {
id: "1",
name: "Europe"
},
item: {
name: "France"
},
score: 4.5
},
{
parent: {
id: "1",
name: "Europe"
},
item: {
name: "UK"
},
score: 4.9
},
{
parent: {
id: "2",
name: "Afrique"
},
item: {
name: "Morocco"
},
score: 3.1
},
{
parent: {
id: "2",
name: "Afrique"
},
item: {
name: "Egypt"
},
score: 3.9
}
];
let group = data.reduce((acc, rec) => {
if (acc.find(item => item.parent.id === rec.parent.id))
{
const idx = acc.findIndex(item => item.parent.id === rec.parent.id)
acc[idx].parent.items = acc[idx].parent.items.concat(rec.item)
acc[idx].parent.score += rec.score
acc[idx].parent.scoreCount +=1
} else {
acc = acc.concat({parent: {...rec.parent, score: rec.score, items: [rec.item], scoreCount:1}})
}
return acc
}, []).map(it => ({parent: {id: it.parent.id, name:it.parent.name, score: (it.parent.score / it.parent.scoreCount), items: it.parent.items}}));
console.log("group", group);
In my angular 8 application, I have 2 arrays:
array1 = [{
"SubType": "2|3|4|5|6",
},
{
"SubType": "2",
},
{
"SubType": "3|4",
},
{
"SubType": "6",
},
{
"SubType": "3|6",
},
]
&
array2 = [{
"id": 2,
"type": "1",
},
{
"id": 3,
"type": "5",
},
{
"id": 4,
"type": "4",
},
{
"id": 5,
"type": "3",
},
{
"id": 6,
"type": "2",
}
]
I am trying to check each "SubType" in array1 and see if that element(id) is present in array2 and if present assign its "type" to a variable. "SubType" is | separated which I and converting to an array using array1..split('|'). This when assigning to a variable will need to be comma separated. I tried using array filter but I am not able to find a way to loop thorough the second array. Can anyone help?
array1.forEach(reg => {
if (reg.SubType) {
let subTypeTemp = reg.SubType.split('|');
let tempVariable = subTypeTemp.some(ele => {
let stringToassign = '';
for (let i = 0; i < array2.length; i++) {
if (ele == array2[i].id) {
stringToassign += array2[i].type + ",";
}
}
})
}
})
const array1 = [
{
SubType: "2|3|4|5|6"
},
{ SubType: "2" },
{ SubType: "3|4" },
{ SubType: "6" },
{ SubType: "3|6" }
];
const array2 = [
{
id: 2,
type: "1"
},
{ id: 3, type: "5" },
{ id: 4, type: "4" },
{ id: 5, type: "3" },
{ id: 6, type: "2" }
];
const array2Obj = array2.reduce(
(acc, curr) => ({
...acc,
[curr.id]: curr.type
}),
{}
);
const types = [];
array1.forEach(item => {
const sub_types = item.SubType.split("|");
sub_types.forEach(st => {
if (st in array2Obj) {
types.push(array2Obj[st]);
}
});
});
const types_str = [...new Set(types)].join(',');
console.log("types", types_str);
You could take a Map and prevent looping array2 over and over for getting type of a wanted id.
var array1 = [{ SubType: "2|3|4|5|6" }, { SubType: "2" }, { SubType: "3|4" }, { SubType: "6" }, { SubType: "3|6" }],
array2 = [{ id: 2, type: "1" }, { id: 3, type: "5" }, { id: 4, type: "4" }, { id: 5, type: "3" }, { id: 6, type: "2" }],
types = new Map(array2.map(({ id, type }) => [id.toString(), type])),
result = array1.map(({ SubType }) => SubType
.split('|')
.map(Map.prototype.get, types)
.join()
);
console.log(result);