Grouping objects with lodash or ES5 - arrays

I'm a javascript noobie, I started to use the library lodash and I'm having some problems. Can someone help me? :/
Input:
var a = [{group: 'A', title: 'test1', value: 'test2'},
{group: 'A', title: 'test2', value: 'test3'},
{group: 'B', title: 'test3', value: 'test4'},
{group: 'B', title: 'test1', value: 'test2'},]
Output:
var a = {A: [
{ title: 'test1', value: 'test2'},
{ title: 'test2', value: 'test3'}
],
B: [
{ title: 'test1', value: 'test2'},
{ title: 'test2', value: 'test3'}
],}

You can group with _.groupBy() and then use _.mapValues() on each group to remove the group property from the objects by mapping them to a new object using _.omit().
const a = [{group: 'A', title: 'test1', value: 'test2'},
{group: 'A', title: 'test2', value: 'test3'},
{group: 'B', title: 'test3', value: 'test4'},
{group: 'B', title: 'test1', value: 'test2'}
];
const result = _(a)
.groupBy('group') // group by the group prop
.mapValues((group) => group.map((o) => _.omit(o, 'group'))) // remove the group prop from each item
.value();
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>

The lodash documentation for group by is pretty explicit you can find it here.
In your case it would look something like this.
_.groupBy(a, 'group')
You can also do it yourself pretty easily as well using the reduce function. It would look something like this:
let groups = a.reduce((grouping, currentItem) => {
if (!grouping[currentItem.group]) {
grouping[currentItem.group] = []
}
grouping[currentItem.group].push(currentItem);
return grouping;
}, {});
A working example can be found here.
In both cases the result would be an object with the groups as property names.
{
"A": [{
"group": "A",
"title": "test1",
"value": "test2"
}, {
"group": "A",
"title": "test2",
"value": "test3"
}],
"B": [{
"group": "B",
"title": "test3",
"value": "test4"
}, {
"group": "B",
"title": "test1",
"value": "test2"
}]
}

Related

Grouping Data in TypeScript Array

I have JSON data that looks like this:
[
{
"id": 1,
"tags": [
"Test 1",
"Test 2",
"Test 3"
]
},
{
"id": 2,
"tags": [
"Test 2",
"Test 3",
"Test 4"
]
},
{
"id": 3,
"tags": [
"Test 3",
"Test 4"
]
}
]
I would like to transform this into data that looks like this:
[
{
"name": "Test 1",
"count": 1
},
{
"name": "Test 2",
"count": 2
},
{
"name": "Test 3",
"count": 3
},
{
"name": "Test 4",
"count": 1
}
]
I can think of some brute ways to do this, but I'm hoping there is something more performant and a little sexier? Possibly using .groupBy() or .reduce()?
Thanks for taking the time to check out my question.
I would:
parse the json
gather all tags in an array
count occurences using one of the approaches in Counting the occurrences / frequency of array elements
interface Item {
id: number,
tags: string[]
}
function countOccurences(a: string[]) {
return a.reduce(function (acc: {[key: string]: number}, curr: string) {
acc[curr] ??= 0;
acc[curr]++;
return acc;
}, {});
}
const data: Item[] = JSON.parse(json);
const tagOccurences = countOccurences(data.flatMap(o => o.tags))
Playground link
You can use reduce inside reduce to group the tags.
const array = [{
id: 1,
tags: ['Test 1', 'Test 2', 'Test 3'],
},
{
id: 2,
tags: ['Test 2', 'Test 3', 'Test 4'],
},
{
id: 3,
tags: ['Test 3', 'Test 4'],
},
];
const frequencies = Object.values(array.reduce((acc, curr) =>
curr.tags.reduce(
(nAcc, tag) => ((nAcc[tag] ??= {name: tag,count: 0}),nAcc[tag].count++,nAcc),
acc
), {}
));
console.log(frequencies);
In TypeScript:
const array = [{
id: 1,
tags: ['Test 1', 'Test 2', 'Test 3'],
},
{
id: 2,
tags: ['Test 2', 'Test 3', 'Test 4'],
},
{
id: 3,
tags: ['Test 3', 'Test 4'],
},
];
type Frequency = {
name: string,
count: number
}
const frequencies = Object.values(array.reduce((acc, curr) =>
curr.tags.reduce(
(nAcc, tag) => ((nAcc[tag] ??= {name: tag,count: 0}),nAcc[tag].count++,nAcc),
acc
), {} as Record<string, Frequency>
));
console.log(frequencies);
Playground
Using for...of iteration and a Map as a cache is a very straightforward approach... and sexy.
TS Playground
type TagsWithId = {
id: number;
tags: string[];
};
type TagCount = {
count: number;
name: string;
};
function verySexyTagCounter (input: TagsWithId[]): TagCount[] {
const map = new Map<string, number>();
for (const {tags} of input) {
for (const name of tags) {
map.set(name, (map.get(name) ?? 0) + 1);
}
}
return [...map.entries()].map(([name, count]) => ({name, count}));
}
const json = `[{"id":1,"tags":["Test 1","Test 2","Test 3"]},{"id":2,"tags":["Test 2","Test 3","Test 4"]},{"id":3,"tags":["Test 3","Test 4"]}]`;
const input: TagsWithId[] = JSON.parse(json);
const result = verySexyTagCounter(input);
console.log(result);

Antd tree table grouped by column value

I need to implement tree table in my react application. that has grouped by an object property value.
The object is as follows
{
"SP": [
{
"DisplayName": "audi",
"Name": "r8",
"Type": "2012"
},
{
"DisplayName": "audi",
"Name": "rs5",
"Type": "2013"
}
],
"Code": [
{
"DisplayName": "ford",
"Name": "mustang",
"Type": "2012"
},
{
"DisplayName": "ford",
"Name": "fusion",
"Type": "2015"
}
],
"Message": [
{
"DisplayName": "kia",
"Name": "optima",
"Type": "2012"
}
]
}
And my table should be as the following image
I have used antd in my project and I tried to implement this functionality with antd table and could not implement as I want. I need the filter functionality too.
Can anyone suggest a solution
You need to restructure your dataSource witch children prop:
function NestedTables() {
return (
<Flexbox>
<Table
size="small"
indentSize={0}
columns={columns}
dataSource={source}
/>
</Flexbox>
);
}
When your source is:
const source = [
{
key: '1',
Code: 'SP',
children: [
{
key: '11',
Code: '5001',
DisplayName: 'audi',
Name: 'r8',
Type: '2012'
},
{
key: '12',
Code: '313',
DisplayName: 'audi',
Name: 'rs5',
Type: '2013'
}
]
},
{
key: '2',
Code: 'Code',
children: [
{
key: '21',
Code: '243',
DisplayName: 'ford',
Name: 'mustang',
Type: '2012'
},
{
key: '22',
Code: '503431',
DisplayName: 'ford',
Name: 'fusion',
Type: '2015'
}
]
},
{
key: '3',
Code: 'Message',
children: [
{
key: '31',
Code: '4311',
DisplayName: 'kia',
Name: 'optima',
Type: '2012'
}
]
}
];
And defined columns filters:
const columns = [
{
title: 'Code',
dataIndex: 'Code',
key: 'Code',
filters: [
{ text: 'SP', value: 'SP' },
{ text: 'Code', value: 'Code' },
{ text: 'Message', value: 'Message' }
],
onFilter: (value, record) => record.Code.indexOf(value) === 0
},
{
title: 'Display Name',
dataIndex: 'DisplayName',
key: 'DisplayName',
filters: [
{ text: 'audi', value: 'audi' },
{ text: 'ford', value: 'ford' },
{ text: 'kia', value: 'kia' }
],
onFilter: (value, record) =>
record.children.filter(child => child.DisplayName === value).length > 0
},
{ title: 'Name', dataIndex: 'Name', key: 'Name' },
{ title: 'Type', dataIndex: 'Type', key: 'Type' }
];
Demo:

How to get transform from object to specific key value pair using lodash?

Products: [
{_id: 'xx1', name: 'p1', sku: 's1'},
{_id: 'xx2', name: 'p2', sku: 's2'},
{_id: 'xx3', name: 'p3', sku: 's3'}
]
I want to replace word '_id' with 'product', and to map to below result:
productArray = [ {product: 'xx1'}, {product: 'xx2'}, {product: 'xx3'} ];
I tried lodash code something like below but it just doesn't seem to work correctly:
let productArray = _.map(Products, '_id');
Can anyone help me on this? Thank you
Why would you even need to use lodash? Map will easily do the job.
const products = [{
_id: 'xx1',
name: 'p1',
sku: 's1'
},
{
_id: 'xx2',
name: 'p2',
sku: 's2'
},
{
_id: 'xx3',
name: 'p3',
sku: 's3'
}
];
const outProducts = products.map(x => {
return {
product: x._id
}
});
console.log(outProducts);

Filter Array of Object from another Array of Object

Currently I'm filtering data based from questions that have checked property value equals to true..
const data = [
{Name: "foo", X1: "1", X2: "1", Other: "Test1"},
{Name: "bar", X1: "2",X2: "2",Other: "Test2"},
{Name: "test",X1: "2",X2: "3",Other: "Test3"}
];
const questions = [{rows: {text: "Text 1", checked: true,fields: "1",column: "X1"}
}, {rows: {text: "Text 2", checked: true,fields: "2",column: "X1"}
}, {rows: {text: "Text 3", checked: false,fields: "1",column: "X2"}
}, {rows: {text: "Text 4", checked: false,fields: "2",column: "X2"}
}, {rows: {text: "Text 5", checked: false,fields: "3",column: "X2"}
}];
console.log(questionArr);
// console.log(dataArr);
const res = data.filter(d => questions.find(f => d[f.rows.column] === f.rows.fields && f.rows.checked));
which works but does not work when filtering the actual data below. I think there's a slight difference between the question object and the actual question object below.. What should be my filter code when accessing these kind of structure ?
I think this is what you're looking for. I matched the data structure to the image in your question. Let me know if I missed something.
const data = [
{ Name: "foo", X1: "1", X2: "1", Other: "Test1" },
{ Name: "bar", X1: "2", X2: "2", Other: "Test2" },
{ Name: "test", X1: "2", X2: "3", Other: "Test3" }
];
const questions = [
{ rows: [{ text: "Text 1", checked: true, fields: "2", column: "X1" }] },
{ rows: [{ text: "Text 2", checked: true, fields: "2", column: "X1" }] },
{ rows: [{ text: "Text 3", checked: false, fields: "1", column: "X2" }] },
{ rows: [{ text: "Text 4", checked: false, fields: "2", column: "X2" }] },
{ rows: [{ text: "Text 5", checked: false, fields: "3", column: "X2" }] }
];
const result = data.filter(function(item){
return questions.some(function(question){
return question.rows.some(function(row){
return (row.checked && item[row.column] === row.fields);
});
});
});
console.log(result);
The compact version
const result = data.filter((item) => questions.some((question) => question.rows.some((row) => (row.checked && item[row.column] === row.fields))));
With perf in mind
const data = [
{ Name: "foo", X1: "1", X2: "1", Other: "Test1" },
{ Name: "bar", X1: "2", X2: "2", Other: "Test2" },
{ Name: "test", X1: "2", X2: "3", Other: "Test3" }
];
const questions = [
{ rows: [{ text: "Text 1", checked: true, fields: "2", column: "X1" }] },
{ rows: [{ text: "Text 2", checked: true, fields: "2", column: "X1" }] },
{ rows: [{ text: "Text 3", checked: false, fields: "1", column: "X2" }] },
{ rows: [{ text: "Text 4", checked: false, fields: "2", column: "X2" }] },
{ rows: [{ text: "Text 5", checked: false, fields: "3", column: "X2" }] }
];
const result = {};
for(let a = 0, b = data.length; a < b; a++){
const item = data[a];
for(let c = 0, d = questions.length; c < d; c++){
const rows = questions[c].rows;
for(let e = 0, f = rows.length; e < f; e++){
const row = rows[e];
if(row.checked && item[row.column] === row.fields){
result[item.Name] = item;
break;
}
}
}
}
// this could be replaced with Object.values(result);
const matches = [];
for(let match in result){
matches.push(result[match]);
}
// not supported by IE yet
// Object.values(result);
console.log(matches);

Currency format in google charts vs angular

I'm trying to add currency format in google charts and angularjs
Google charts display Money not Percentages
var formatter = new google.visualization.NumberFormat({
prefix: '$' }); formatter.format(data, 1);
var options = {
pieSliceText: 'value' };
how to add above code in the function below ?
$scope.jkchart = function(){
$scope.chartObject = {};
$scope.chartObject.data = {"cols": [
{id: "t", label: "Topping", type: "string"},
{id: "s", label: "Slices", type: "number"}
], "rows": [
{c: [
{v: "Mushrooms"},
{v: 3},
]},
{c: [
{v: "Olives"},
{v: 31}
]},
{c: [
{v: "Zucchini"},
{v: 5},
]}
]};
$scope.chartObject.type = "PieChart";
$scope.chartObject.options = {
'title': 'How Much Pizza I Ate Last Night'
};
};
You could specify format like this:
$scope.chartObject.formatters = {
number: [{
columnNum: 1,
prefix: '$'
}]
};
Working example
var app = angular.module('chartApp', ['googlechart']);
app.controller('MainCtrl', function ($scope) {
$scope.chartObject = {};
$scope.chartObject.data = {
"cols": [
{ id: "t", label: "Topping", type: "string" },
{ id: "s", label: "Slices", type: "number" }
], "rows": [
{
c: [
{ v: "Mushrooms" },
{ v: 3 },
]
},
{
c: [
{ v: "Olives" },
{ v: 31 }
]
},
{
c: [
{ v: "Zucchini" },
{ v: 5 },
]
}
]
};
$scope.chartObject.type = "PieChart";
$scope.chartObject.options = {
'title': 'How Much Pizza I Ate Last Night',
pieSliceText: 'value'
};
$scope.chartObject.formatters = {
number: [{
columnNum: 1,
prefix: '$'
}]
};
});
<script src="http://code.angularjs.org/1.2.10/angular.js"></script>
<script src="http://bouil.github.io/angular-google-chart/ng-google-chart.js"></script>
<body ng-app='chartApp' ng-controller="MainCtrl">
<div google-chart chart="chartObject" style="width: 900px; height: 500px;"></div>
</body>

Resources