javascript - Grouping elements in array by properties - javascript

I am trying to filter same color objects from the below json and every color value is containing combination of two values(color and numeric value) but I just want to filter on the basis of color.
Here what i have tried
var _ = require('underscore-plus');
var data = [{
"name": "jim",
"color": "blue 1",
"age": "22"
}, {
"name": "Sam",
"color": "blue 2",
"age": "33"
}, {
"name": "eddie",
"color": "green 1",
"age": "77"
},
{
"name": "Dheeraj",
"color": "blue 3",
"age": "25"
},
{
"name": "Suraj",
"color": "green 1",
"age": "25"
}
];
var result=_.groupBy(data,"color");
console.log(result)
Result should be array of Objects having same color.
[{ "name": "jim", "color": "blue 1", "age": "22" },
{ "name": "Sam", "color": "blue 2", "age": "33" },
{ "name": "Dheeraj", "color": "blue 3", "age": "25" }]
and
[{ "name": "Suraj", "color": "green 1", "age": "25" },
{ "name": "eddie", "color": "green 1", "age": "77" }]

You can group the items using Array.prototype.reduce:
var data = [{
"name": "jim",
"color": "blue 1",
"age": "22"
}, {
"name": "Sam",
"color": "blue 2",
"age": "33"
}, {
"name": "eddie",
"color": "green 1",
"age": "77"
}, {
"name": "Dheeraj",
"color": "blue 3",
"age": "25"
}, {
"name": "Suraj",
"color": "green 1",
"age": "25"
}];
var result = data.reduce(function(grouped, obj) {
var key = obj.color.split(' ')[0]; // get the color from the key
grouped[key] = (grouped[key] || []).concat(obj); // use the existing array or create a new array, add the object to it, and assign it to the grouped object
return grouped; // return the grouped object
}, {});
console.log(result);

You could group by the color.
var data = [{ "name": "jim", "color": "blue 1", "age": "22" }, { "name": "Sam", "color": "blue 2", "age": "33" }, { "name": "eddie", "color": "green 1", "age": "77" }, { "name": "Dheeraj", "color": "blue 3", "age": "25" }, { "name": "Suraj", "color": "green 1", "age": "25" }],
grouped = {},
colors;
data.forEach(function (a) {
var group = a.color.match(/^[a-z]+/i);
grouped[group] = grouped[group] || [];
grouped[group].push(a);
});
colors = Object.keys(grouped);
colors.forEach(function (color) {
console.log(color, grouped[color]);
});
.as-console-wrapper { max-height: 100% !important; top: 0; }

you can use Jquery.grep() for example
var result = $.grep(data, function(n){ return n.color == "blue 3" })

Simply using the groupBy function as detailed by underscore docs here:
var result = _.groupBy(data, function(datum) { return datum.color; });
You need to provide a function to be used, that will return the attribute to group the elements on, which is color in this case.
If you want to filter instead, as is mentioned in the question, you can use the underscore filter method:
var blueOne = _.filter(data, function(datum){ return datum.color == 'blue 1'; });

Related

How Do I Pivot An Array Of Objects in JavaScript then display on HTML Table

I have an array of objects which I need to convert to a table (kind of pivoting the data). Saying this I need to get another array of objects with unique titles which have nested arrays of objects with pairs of the values then add the content vertically and horizontally. (Please refer to the image)
Thanks in advance for any suggestions.
I have been able to generate an array of pivoted values but I will need to display them in a table and carry out some addition
Find here the JSON Data
const data=[{
"Id": 1,
"Grade": "Grade A",
"category": "Grains",
"subCategory": "Maize",
"sales": 1200
},
{
"Id": 2,
"Grade": "Grade B",
"category": "Grains",
"subCategory": "Wheat",
"sales": 130
},
{
"Id": 3,
"Grade": "Grade B",
"category": "Grains",
"subCategory": "Banana",
"sales": 1200
},
{
"Id": 4,
"Grade": "Grade C",
"category": "Grains",
"subCategory": "Apple",
"sales": 1400
},
{
"Id": 5,
"Grade": "Grade A",
"category": "Grains",
"subCategory": "Maize",
"sales": 1200
},
{
"Id": 6,
"Grade": "Grade B",
"category": "Grains",
"subCategory": "Wheat",
"sales": 130
},
{
"Id": 7,
"Grade": "Grade B",
"category": "Grains",
"subCategory": "Banana",
"sales": 1200
},
{
"Id": 8,
"Grade": "Grade C",
"category": "Grains",
"subCategory": "Apple",
"sales": 1400
},
{
"Id": 7,
"Grade": "Grade B",
"category": "Grains",
"subCategory": "Banana",
"sales": 1200
},
{
"Id": 8,
"Grade": "Grade C",
"category": "Grains",
"subCategory": "Apple",
"sales": 1400
}]
let pivoted = data.reduce((prev, cur) => {
let existing = prev.find(x => x.title === cur.title);
if (existing)
existing.values.push(cur)
else
prev.push({
title: cur.title,
values: [cur]
});
return prev;
}, []);
console.log(pivoted);
If you are open to using a table library such as Tabulator, here is an example. I added couple more data points to show functionality. I am sure there is a simpler way but hope this helps.
const data = [{
"Id": 1,
"Grade": "Grade A",
"category": "Grains",
"subCategory": "Maize",
"sales": 1200
},
{
"Id": 2,
"Grade": "Grade B",
"category": "Grains",
"subCategory": "Wheat",
"sales": 130
},
{
"Id": 3,
"Grade": "Grade B",
"category": "Fruits",
"subCategory": "Banana",
"sales": 1200
},
{
"Id": 4,
"Grade": "Grade C",
"category": "Fruits",
"subCategory": "Apple",
"sales": 1400
},
{
"Id": 5,
"Grade": "Grade A",
"category": "Grains",
"subCategory": "Maize",
"sales": 1200
},
{
"Id": 6,
"Grade": "Grade B",
"category": "Grains",
"subCategory": "Wheat",
"sales": 130
},
{
"Id": 7,
"Grade": "Grade B",
"category": "Fruits",
"subCategory": "Banana",
"sales": 1200
},
{
"Id": 8,
"Grade": "Grade C",
"category": "Fruits",
"subCategory": "Apple",
"sales": 1400
},
{
"Id": 7,
"Grade": "Grade B",
"category": "Fruits",
"subCategory": "Banana",
"sales": 1200
},
{
"Id": 8,
"Grade": "Grade C",
"category": "Fruits",
"subCategory": "Apple",
"sales": 1400
},
{
"Id": 9,
"Grade": "Grade C",
"category": "Fruits",
"subCategory": "Banana",
"sales": 1000
},
{
"Id": 10,
"Grade": "Grade A",
"category": "Grains",
"subCategory": "Wheat",
"sales": 500
}
];
var grades = new Set(data.map(i => i.Grade)); //Get list of grades
grades.forEach(grade => grades[grade] = 0); // Initialize grade amount to 0
// Insert grades into each data item
var tableData = data.map((item) => ({
...item,
...grades,
[item.Grade]: item.sales
}));
// Group and sum each item in data by category, subCategory and grades
var pivotTable = [...tableData.reduce((r, o) => {
const key = o.category + '-' + o.subCategory + '-' + Array.from(grades).join('-');
const item = r.get(key) || Object.assign({}, o, grades);
grades.forEach(grade => item[grade] += o[grade]);
delete item.Grade;
return r.set(key, item);
}, new Map).values()];
// Prep columns to add into Tabulator for each grade
var gradeCols = [];
grades.forEach(grade => {
var col = {
title: grade,
field: grade,
bottomCalc: "sum"
}
gradeCols.push(col);
});
// Hide repeating row cells
var pivotFormatter = (row) => {
try {
if (row.getPosition(true) != 0) {
var data = row.getCells();
var prev = row.getPrevRow().getCells();
var pivotColumns = 2;
for (i = 0; i < pivotColumns; i++) {
if (data[i].getValue() == prev[i].getValue() && data[0].getValue() == prev[0].getValue()) {
data[i].getElement().style.visibility = "hidden";
}
}
}
} catch (e) {}
}
// Tabulator table
var table = new Tabulator("#table", {
height: '100%',
layout: "fitColumns",
headerSort: false,
rowFormatter: (row) => pivotFormatter(row),
columns: [{
title: "Category",
field: "category"
},
{
title: "SubCategory",
field: "subCategory",
},
...gradeCols,
{
title: "Total",
field: "total",
mutator: (value, data, type, params, component) => Array.from(grades).reduce((acc, item) => acc + data[item], 0),
bottomCalc: "sum"
}
]
});
// Set tabulator data
table.setData(pivotTable);
html, body {font-size: 12px;}
.tabulator {font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji;}
<link href="https://cdnjs.cloudflare.com/ajax/libs/tabulator/4.9.3/css/bootstrap/tabulator_bootstrap4.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/tabulator/4.9.3/js/tabulator.min.js"></script>
<div id="table"></div>

Loop Array of Object and connect them together by different keys

I'm trying to loop thru products array and find its description in description array. The product id and description parent represent the same product. If the description could be found, push the product with its description to the results array.
I don't really know how this loop should look like.
Products
let products = [
{
"id": "43",
"titel": "Phone",
"price": "211"
},{
"id": "76",
"titel": "Battery",
"price": "34"
},{
"id": "102",
"titel": "Pen",
"price": "45"
},{
"id": "127",
"titel": "Apple",
"price": "10"
}
]
Descriptions
let descriptions= [
{
"description": "Good battery",
"parent": "76"
},{
"description": "Sharp pen",
"parent": "102"
},
]
Expected output results
let results = [
{
"id": "76",
"titel": "Battery",
"price": "34"
"description": "Good battery",
"parent": "76"
},{
"id": "102",
"titel": "Pen",
"price": "45"
"description": "Sharp pen",
"parent": "102"
},
]
You can take advantage of Array.prototype.reduce which allows to transform an array to another type of Object of array.
combine with the Array.prototype.find to check if item with a given id exists in the descriptions Array
let products = [
{
"id": "43",
"titel": "Phone",
"price": "211"
},{
"id": "76",
"titel": "Battery",
"price": "34"
},{
"id": "102",
"titel": "Pen",
"price": "45"
},{
"id": "127",
"titel": "Apple",
"price": "10"
}
]
let descriptions= [
{
"description": "Good battery",
"parent": "76"
},{
"description": "Sharp pen",
"parent": "102"
},
]
const result = products.reduce((acc, item) => {
// Check if the description of the current product exists
let exist = descriptions.find(desc => {
return item.id === desc.parent;
});
if(exist) {
return acc.concat({...item, description: exist.description});
}
return acc;
}, []);
console.log(result);
If you add another array comments you can update the code instead of to concatenate the item in the accumulator directly you'll create another object which after finding the related comment in the comment array you'll add the comment key in that object with their comment.
Here is the code
let products = [
{
"id": "43",
"titel": "Phone",
"price": "211"
},{
"id": "76",
"titel": "Battery",
"price": "34"
},{
"id": "102",
"titel": "Pen",
"price": "45"
},{
"id": "127",
"titel": "Apple",
"price": "10"
}
]
let descriptions= [
{
"description": "Good battery",
"parent": "76"
},{
"description": "Sharp pen",
"parent": "102"
},
]
let comments = [
{
"comment": "Good battery comment",
"product": "76",
}, {
"comment": "Sharp pen comment",
"product": "102"
}
];
const result = products.reduce((acc, item) => {
// Check if the description of the current product exists
let productExists = descriptions.find(desc => {
return item.id === desc.parent;
});
let commentExists = comments.find(comment => {
return item.id === comment.product
});
let newItem = null;
if(productExists) {
newItem = {
...item,
description: productExists.description
};
}
if(commentExists) {
newItem.comment = commentExists.comment;
}
return newItem? acc.concat(newItem): acc;
}, []);
console.log(result);
You should iterate over the descriptions, then use there Array.find and merge them together into a new object, with Object.assign and push them to your results
let products = [
{
"id": "43",
"titel": "Phone",
"price": "211"
},{
"id": "76",
"titel": "Battery",
"price": "34"
},{
"id": "102",
"titel": "Pen",
"price": "45"
},{
"id": "127",
"titel": "Apple",
"price": "10"
}
];
let descriptions= [
{
"description": "Good battery",
"parent": "76"
},{
"description": "Sharp pen",
"parent": "102"
},
];
let results = [];
for (const desc of descriptions) {
const product = products.find(v => v.id === desc.parent);
if (!product) {
continue;
}
results.push(Object.assign({}, product, desc));
}
console.log(results);
const result = descriptions.map(descr => {
const product = products.find(prod => prod.id === descr.parent);
return {...product, ...descr}
})

Map/Transform javascript array of objects using lodash

I have an array of object that looks like this:
[
{
"group": {
"name": "Software Developers",
"desc":"some desc"
},
"name": "devs 1",
"file": "f1.csv"
},
{
"group": {
"name": "Software Developers",
"desc":"some desc"
},
"name": "devs 2",
"file": "f2.csv"
},
{
"group": {
"name": "Ceos",
"desc":"some desc"
},
"name": "ceos 1",
"file": "f3.csv"
},
{
"group": {
"name": "Ceos",
"desc":"some desc"
},
"name": "ceos 2",
"file": "f4.csv"
}
]
Using lodash how to transform this json array to be like this:
[
{
"group": {
"name": "Software Developers",
"desc":"some desc"
},
lists: [{ "name": "devs 1", "file": "f1.csv" }, { "name": "devs 2", "file": "f2.csv" }]
},
{
"group": {
"name": "Ceos",
"desc":"some desc"
},
lists: [{ "name": "ceos 1", "file": "f3.csv" }, { "name": "ceos 2", "file": "f4.csv" }]
}
]
I tried various solutions but without any luck.
_.groupBy() the group.name, and then _.map() to the desired structure:
const data = [{"group":{"name":"Software Developers","desc":"some desc"},"name":"devs 1","file":"f1.csv"},{"group":{"name":"Software Developers","desc":"some desc"},"name":"devs 2","file":"f2.csv"},{"group":{"name":"Ceos","desc":"some desc"},"name":"ceos 1","file":"f3.csv"},{"group":{"name":"Ceos","desc":"some desc"},"name":"ceos 2","file":"f4.csv"}];
const result = _.flow([
(arr) => _.groupBy(arr, 'group.name'),
(groups) => _.map(groups, (g) => ({
..._.head(g).group,
lists: _.map(g, o => _.pick(o, ['name', 'file']))
}))
])(data);
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
You could also achieve this without lodash via ES6 with Object.entries, reduce & map:
const data = [{ "group": { "name": "Software Developers", "desc": "some desc" }, "name": "devs 1", "file": "f1.csv" }, { "group": { "name": "Software Developers", "desc": "some desc" }, "name": "devs 2", "file": "f2.csv" }, { "group": { "name": "Ceos", "desc": "some desc" }, "name": "ceos 1", "file": "f3.csv" }, { "group": { "name": "Ceos", "desc": "some desc" }, "name": "ceos 2", "file": "f4.csv" } ]
const result = Object.entries(data.reduce((r,c) =>
(r[c.group.name] = [...r[c.group.name] || [], c], r), {}))
.reduce((r,[k,v]) => (r.push({ group: v[0].group, lists: v.map(({name, file}) =>
({ name, file })) }), r), [])
console.log(result)

Javascript count between 2 arrays

I have 2 arrays objects and I need to do a count of home many types of card we have.
The first object contains all the car id's and the second list contains the types of cars.
Here is the data:
var arr = {
"categories": [{
"id": "100",
"name": "category name",
"car_id": "1"
}, {
"id": "192",
"name": "category name here",
"car_id": "25"
}, {
"id": "192",
"name": "category name here",
"car_id": "27"
}]
};
var arr2 = {
"cars": [{
"id": "1",
"name": "car name",
"car_id": "1",
"type": "ford"
}, {
"id": "4",
"name": "name 2",
"car_id": "25",
"type": "ford"
}, {
"id": "4",
"name": "name 2",
"car_id": "27",
"type": "fiat"
}]
};
There's only 5 types of cars so I have 5 variables:
var:
ford,
fiat,
mazda,
mini,
mg
So, what I need to end up with is something like this:
ford: 2;
fiat: 1;
mazda: 0;
mini: 0;
mg: 0;
How can I do this?
If your number of types are fixed, then try this approach
Make an map first
var map = {
ford: 0,
fiat: 0,
mazda: 0,
mini: 0,
mg: 0
};
Now iterate the arrays and count by types
arr2.cars.forEach( function( item ){
map[ item.type ]++;
});
your map is populated with the values now.
var arr2 = {
"cars": [{
"id": "1",
"name": "car name",
"car_id": "1",
"type": "ford"
}, {
"id": "4",
"name": "name 2",
"car_id": "25",
"type": "ford"
}, {
"id": "4",
"name": "name 2",
"car_id": "27",
"type": "fiat"
}]
};
var map = {
ford: 0,
fiat: 0,
mazda: 0,
mini: 0,
mg: 0
};
arr2.cars.forEach(function(item) {
map[item.type] ++;
});
console.log(map);
var arr = {
"categories": [{
"id": "100",
"name": "category name",
"car_id": "1"
}, {
"id": "192",
"name": "category name here",
"car_id": "25"
}, {
"id": "192",
"name": "category name here",
"car_id": "27"
}]
};
var arr2 = {
"cars": [{
"id": "1",
"name": "car name",
"car_id": "1",
"type": "ford"
}, {
"id": "4",
"name": "name 2",
"car_id": "25",
"type": "ford"
}, {
"id": "4",
"name": "name 2",
"car_id": "27",
"type": "fiat"
}]
};
var carCount, typeCount;
arr.categories.forEach(function(item){
if(item.hasOwnProperty("car_id")){
carCount = arr.categories.length;
}
});
arr2.cars.forEach(function(item){
if(item.hasOwnProperty("type")){
typeCount = arr2.cars.length;
}
});
console.log(carCount);
console.log(typeCount);
https://jsfiddle.net/Law7rzc2/
All you need is
countBy(arr2.cars, 'type')
The implementation of countBy is left as an exercise.
Array.prototype.reduce is your go-to for this kind of array computation. A Map will help you keep track of unique car makes as you iterate thru the array of cars.
var obj2 = {
"cars": [{
"id": "1",
"name": "car name",
"car_id": "1",
"type": "ford"
}, {
"id": "4",
"name": "name 2",
"car_id": "25",
"type": "ford"
}, {
"id": "4",
"name": "name 2",
"car_id": "27",
"type": "fiat"
}]
};
const typeStat = cars => {
let map = cars.reduce((m, {type}) =>
m.set(type, (m.get(type) || 0) + 1), new Map());
return Array.from(map, ([make, count]) => ({make, count}));
};
let stats = typeStat(obj2.cars)
console.log(stats);
Output
[
{
"make": "ford",
"count": 2
},
{
"make": "fiat",
"count": 1
}
]

mongodb getting the oldest of animals map/reduce

I've never tried map/reduce.
How would I get the oldest of each type of animal?
My data is like this:
[
{
"cateory": "animal",
"type": "cat",
"age": 4,
"id": "a"
},
{
"cateory": "animal",
"type": "bird",
"age": 3,
"id": "b"
},
{
"cateory": "animal",
"type": "cat",
"age": 7
"id": "c"
},
{
"cateory": "animal",
"type": "bird",
"age": 4,
"id": "d"
},
{
"cateory": "animal",
"type": "cat",
"age": 8,
"id": "e"
},
{
"cateory": "company",
"type": "Internet",
"age": 5,
"id": "Facebook"
}
]
I'm using node-mongodb-native. Thanks!
Your map function should look something like this:
map = function() {
emit({type: this.type}, {age: this.age});
}
And the reduce function:
reduce = function(key, values) {
maxAge = 0;
values.forEach(function(v) {
if (maxAge < v['age']) {
maxAge = v['age'];
}
});
return {age: maxAge};
}
It's pretty simple:
collection.find({type : 'animal'}).sort({animal: -1}).limit(1);

Categories