groupBy and value as key - lodash

I have following data:
var groups = {
  0: { 'GroupID': 1, 'AttributeID': 'Agitator', 'Value': 'No' },
  1: { 'GroupID': 1, 'AttributeID': 'ATG', 'Value': 'Custodian' },
2: { 'GroupID': 1, 'AttributeID': 'Heating Coil', 'Value': 'Mild Steel' },
3: { 'GroupID': 2, 'AttributeID': 'Agitator', 'Value': 'Yes' },
  4: { 'GroupID': 2, 'AttributeID': 'ATG', 'Value': 'Non Custodian' },
5: { 'GroupID': 2, 'AttributeID': 'Heating Coil', 'Value': 'Mild Steel' },
};
I would like to group the data by GroupID and use one of the value as key for another value. The expected outcome will be as following. Is this achievable?
var data = {
  0: { 'GroupID': 1, 'Agitator': 'No', 'ATG': 'Custodian', 'Heating Coil': 'Mild Steel' },
1: { 'GroupID': 2, 'Agitator': 'Yes', 'ATG': 'Non Custodian', 'Heating Coil': 'Mild Steel' }
};
So far what I have tried is as below. I have no idea how to make one of value as key for another value.
_.chain(groups).groupBy('GroupID').map((value, key) => {
return {
GroupID: key,
AttributeID: _.map(value, 'AttributeID'),
Value: _.map(value, 'Value')
}
}).value();
The result looks like below.
var data = {
  0: { 'GroupID': 1, 'AttributeID': ['Agitator', 'ATG', 'Heating Coil'], 'Value': ['No', 'Custodian', 'Mild Steel'] },
1: { 'GroupID': 2, 'AttributeID': ['Agitator', 'ATG', 'Heating Coil'], 'Value': ['Yes', 'Non Custodian', 'Mild Steel'] }
};
Appreciate any help.

You got very close to solving your own question. Well done! Let's break this into parts and see what's missing.
You correctly identified that _.groupBy is what you need in order to collect objects with the same GroupID.
var grouped = _.groupBy(groups, 'GroupID');
// { '1':
// [ { GroupID: 1, AttributeID: 'Agitator', Value: 'No' },
// { GroupID: 1, AttributeID: 'ATG', Value: 'Custodian' },
// { GroupID: 1, AttributeID: 'Heating Coil', Value: 'Mild Steel' } ],
// '2':
// [ { GroupID: 2, AttributeID: 'Agitator', Value: 'Yes' },
// { GroupID: 2, AttributeID: 'ATG', Value: 'Non Custodian' },
// { GroupID: 2, AttributeID: 'Heating Coil', Value: 'Mild Steel' } ] }
You also correctly identified _.map as the function that lets you collect the values of a particular property across an array of objects.
var firstGroupAttributes = _.map(grouped['1'], 'AttributeID');
// [ 'Agitator', 'ATG', 'Heating Coil' ]
The function you wrote to construct the final object for each group produced the following object for the first group:
{
'GroupID': 1,
'AttributeID': ['Agitator', 'ATG', 'Heating Coil'],
'Value': ['No', 'Custodian', 'Mild Steel']
}
Conveniently, the AttributeID and Value properties have the same order, so the first element of the Value array corresponds to the first element of the AttributeID array, the second to the second, and so forth. Underscore has a function called _.object1, which can take such a pair of arrays and return an object where each element of the first array names a property and the corresponding element of the second array provides its value:
_.object(['Agitator', 'ATG'], ['No', 'Custodian']);
// { Agitator: 'No',
// ATG: 'Custodian', }
Having taken care of the attributes and their values, now we just need to add the GroupID to the object. There are several similar but slightly different functions that work for this purpose, such as _.extend and _.defaults:
_.extend({ a: 1 }, { b: 2 });
// { a: 1, b: 2 }
Putting these ingredients together, the function that we map over grouped could look something like this:
function mergeGroup(value, key) {
var attributes = _.map(value, 'AttributeID');
var values = _.map(value, 'Value');
return _.chain(attributes).object(values).extend({ GroupID: key }).value();
}
If you use _.map, as you did, you get an array with the final objects.
var data = _.chain(groups).groupBy('GroupID').map(mergeGroup).value();
// [ { Agitator: 'No',
// ATG: 'Custodian',
// 'Heating Coil': 'Mild Steel',
// GroupID: '1' },
// { Agitator: 'Yes',
// ATG: 'Non Custodian',
// 'Heating Coil': 'Mild Steel',
// GroupID: '2' } ]
Alternatively, you could use _.mapObject2 to produce an object where each key is the same as the GroupID:
var data = _.chain(groups).groupBy('GroupID').mapObject(mergeGroup).value();
// { '1':
// { Agitator: 'No',
// ATG: 'Custodian',
// 'Heating Coil': 'Mild Steel',
// GroupID: '1' },
// '2':
// { Agitator: 'Yes',
// ATG: 'Non Custodian',
// 'Heating Coil': 'Mild Steel',
// GroupID: '2' } }
1 Lodash originally inherited _.object from Underscore, but they added an alias called _.zipObject and in version 4, they decided to remove the original name _.object from their interface. So you'd have to call _.zipObject if using Lodash. Underscore's _.object can also work with an array of pairs instead of a pair of arrays; in Lodash 4, you'd use _.fromPairs for that instead.
2 _.mapValues in Lodash.

Related

My prop array isn't setting my data array value

I'm ultimately attempting to set the initial value of a multi select using an array I'm passing in via prop. But for whatever reason I can't get the initial value to stick onto my data property. I'm new to Vue and feel like there's something I'm fundamentally not grasping.
My prop is coming in properly with the correct values (initialroutes).
However, my data property is not getting the prop's values (rts).
Here is my code:
<v-select
v-model="rts"
:items="getRoutesOptions(item.CenterID)"
item-text="sRoute"
item-value="RouteID"
:multiple="true"
></v-select>
props: {
initialroutes: {
type: Array,
required: true
}
},
data() {
return {
rts: this.initialroutes
}
}
`
The values of the <v-select>.v-model must exactly match (including its type) the values found in <v-select>.items[]. The initialroutes[] values are numbers, so the values in <v-select>.items must also be numbers.
You probably have string values for items[].RouteID:
[
{ sRoute: 'A', RouteID: '1' },
{ sRoute: 'B', RouteID: '2' },
{ sRoute: 'C', RouteID: '3' },
{ sRoute: 'D', RouteID: '4' },
]
...which don't exactly match up to the numbers found in initialroutes[] ([1,2]).
You can either convert initialroutes[] to strings, or items[].RouteID to numbers:
[
{ sRoute: 'A', RouteID: 1 },
{ sRoute: 'B', RouteID: 2 },
{ sRoute: 'C', RouteID: 3 },
{ sRoute: 'D', RouteID: 4 },
]
demo

Apex Line Area chart is not getting displayed on the page in Vuejs

I am stuck on a page where i am not able to display the charts on the page.
To make it simplify what I have done is, here is the code sandbox:
I see there an error in console about the data, I am not sure about it.
https://codesandbox.io/s/compassionate-snyder-bckoq
I want to display the chart like this (as an example), but I am not able to display on the code sandbox
Please help.
The format of series is not aligned with ApexCharts.
You need to transform the data to match with ApexChart format.
Please see the changes in the codesandbox.
https://codesandbox.io/s/small-dew-eztod?file=/src/components/HelloWorld.vue
options: {
// X axis labels
xaxis: {
type: 'date',
categories: ["2021-05-04", "2021-05-05", "2021-05-07"]
},
},
series: [
{
name: "total",
data: [2, 2, 1],
},
{
name: "pending",
data: [0, 1, 0],
},
{
name: "approved",
data: [2, 1, 1],
},
{
name: "rejected",
data: [0, 0, 0],
},
],
Transform data to fit ApexChart
const data = {
"2021-05-04": {
total: 2,
pending: 0,
approved: 2,
rejected: 0,
},
"2021-05-05": {
total: 2,
pending: 1,
approved: 1,
rejected: 0,
},
"2021-05-07": {
total: 1,
pending: 0,
approved: 1,
rejected: 0,
},
};
const xaxis = {
type: "date",
categories: Object.keys(data).map((key) => key), // ['2021-05-04', '2021-05-05', '2021-05-07']
};
let statusObj = [];
for (const dataValue of Object.values(data)) { // get the values from keys '2021-05-04', '2021-05-05' ...
// loop the values, e.g. 1st loop: { total: 2, pending: 0, approved: 2, rejected: 0, }
for (const [key, value] of Object.entries(dataValue)) {
// take 'total' as example, find if statusObj already has { name: 'total', data: [x] }, e.g. statusObj = { name: 'total', data: [1] }
const existingStatusIndex = Object.keys(statusObj).find(
(sKey) => statusObj[sKey].name === key
);
// if yes, return the index of it
if (existingStatusIndex) {
// add new data value to existing data object. e.g. { name: 'total', data: [1, 2] }
statusObj[existingStatusIndex].data.push(value);
continue;
}
// if no, create a new object and add it to statusObj
statusObj.push({
name: key,
data: [value],
});
}
}
Output:
xaxis {
type: 'date',
categories: [ '2021-05-04', '2021-05-05', '2021-05-07' ]
}
statusObj [
{ name: 'total', data: [ 2, 2, 1 ] },
{ name: 'pending', data: [ 0, 1, 0 ] },
{ name: 'approved', data: [ 2, 1, 1 ] },
{ name: 'rejected', data: [ 0, 0, 0 ] }
]

lo-dash : JSON parsing and returning required output

I am new to lodash and have below queries. i have attach a sample json of mine. I referred many other post but nothing worked for me
i want to find array with state as true and return below values
name only
name and partno
i also want to just return state and name
[{
'state': true,
'partno': 21,
'idno': 1,
'name': 'abc'
},
{
'state': true,
'partno': 22,
'idno': 2,
'name': 'xyz'
},
{
'state': false,
'partno': 23,
'idno': 3,
'name': 'mnd'
},
{
'state': true,
'partno': 26,
'idno': 4,
'name': 'koi'
},
{
'state': false,
'partno': 21,
'idno': 1,
'name': 'abc'
}
]
The below code does what you need
const data = getData();
console.log('I want to...\n\n');
console.log('1. find array with state as true and return name only')
const result1 = _(data)
.filter('state')
.map('name')
.value();
// logAsJson is a helper function below
logAsJson(result1);
console.log('2. find array with state as true and return name and partno');
const result2 = _(data)
.filter('state')
// pickProperties is a helper function below
.map(pickProperties(['name', 'partno']))
.value();
logAsJson(result2);
console.log('3. just return state and name');
const result3 = _.map(data, pickProperties(['state', 'name']));
logAsJson(result3);
//
// Helper functions
//
function logAsJson(anObject) {
console.log(JSON.stringify(anObject, null, 2));
}
function pickProperties(propsToPick) {
return function mapperFn(anObject) {
return _.pick(anObject, propsToPick);
};
}
function getData() {
return [
{
'state': true,
'partno': 21,
'idno': 1,
'name': 'abc'
},
{
'state': true,
'partno': 22,
'idno': 2,
'name': 'xyz'
},
{
'state': false,
'partno': 23,
'idno': 3,
'name': 'mnd'
},
{
'state': true,
'partno': 26,
'idno': 4,
'name': 'koi'
},
{
'state': false,
'partno': 21,
'idno': 1,
'name': 'abc'
}
];
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
Maybe I didn't get your question, but here is what you requested:
//Filters your array and creates a new object
yourArray.filter(x => x.state)
.map( x => ({'name':x.name, 'partno':x.partno}))
I didn't know lodash, but taking a look at the documentation, I came with this:
_map(_.filter(yourArray, 'state'), (x) => ({'name':x.name,'partno':x.partno})
Which is pretty much the same. Is there a reason why are you using lodash for this?

How to remove fields from collection with Lodash?

var characters = [
{ 'name': 'barney', 'blocked': 'a', 'employer': 'slate' },
{ 'name': 'fred', 'blocked': 'a', 'employer': 'slate' },
{ 'name': 'pebbles', 'blocked': 'a', 'employer': 'na' },
{ 'name': 'pebbles', 'blocked': 'b', 'employer': 'hanna' },
{ 'name': 'wilma', 'blocked': 'c', 'employer': 'barbera' },
{ 'name': 'bam bam', 'blocked': 'c', 'employer': 'barbera' }
];
Using lodash, is there an elegant way to remove specific fields from this collection?
Simply do:
var filteredCollection = _.map(characters, function (c) {
return _.omit(c, ['name']);
});
Now, filteredCollection no longer have the field 'name'.
Here is a jsfiddle.
Note:
If you use NodeJS with Mongoose v4+, you must use: _.omit(c._doc, ['name']);

Can I customize the tooltip text in a Google Geochart chart?

Below is the code I'm using, based on a how to I found in Google's documentation, but I'm not sure if it applies to the Geochart, if I'm doing it right, or if there is some other way to do it for a Geochart.
This code works fine if I don't include the tooltip column. When I do, I get the error "Incompatible data table: Error: Table contains more columns than expected (Expecting 2 columns)," displayed where the Geochart should be.
This question addresses the same issue, but not specifically for a Geochart.
<script type='text/javascript' src='https://www.google.com/jsapi'></script>
<script type='text/javascript'>
google.load( 'visualization', '1', { 'packages': ['geochart'] } );
google.setOnLoadCallback( drawRegionsMap );
function drawRegionsMap()
{
var data = google.visualization.arrayToDataTable([
[ 'State', 'Relevance', {type: 'string', role: 'tooltip'} ],
[ 'Alabama', 3, 'tooltip test text' ],
[ 'Arizona', 1, 'tooltip test text' ],
]);
var options =
{
region: 'US',
resolution: 'provinces',
};
var chart = new google.visualization.GeoChart( document.getElementById( 'chart_div' ) );
chart.draw( data, options );
};
</script>
I was able to include the text i wanted in the tooltip, including the values this way:
var data = new google.visualization.DataTable();
data.addColumn('string', 'Country'); // Implicit domain label col.
data.addColumn('number', 'Value'); // Implicit series 1 data col.
data.addColumn({type:'string', role:'tooltip'}); //
data.addRows([
[{v:"US",f:"United States of America"},1,"21% of Visits"],
[{v:"GB",f:"United Kingdom"},2,"7% of visits"],
[{v:"DE",f:"Germany"},3,"6% of visits"],
[{v:"FR",f:"France"},4,"5% of visits"],
[{v:"ES",f:"Spain"},5,"5% of visits"],
[{v:"CA",f:"Canada"},6,"4% of visits"],
[{v:"IT",f:"Italy"},7,"4% of visits"],
[{v:"NL",f:"The Netherlands"},8,"4% of visits"],
[{v:"BR",f:"Brazil"},9,"4% of visits"],
[{v:"TR",f:"Turkey"},10,"3% of visits"],
[{v:"IN",f:"India"},11,"3% of visits"],
[{v:"RU",f:"Russia"},12,"3% of visits"],
[{v:"AU",f:"Australia"},13,"2% of visits"],
]);
This way, for example "United States of America" would be line 1 of the tooltip, and "21% of visits" would be the second line. With this format I can include whatever text I want in the tooltip, in both lines.
In order to customize the tooltip to use html including new line / images please check this example:
http://jsfiddle.net/SD4KA/
google.load('visualization', '1.1', {packages: ['geochart'], callback: drawVisualization});
function drawVisualization() {
var data = google.visualization.arrayToDataTable([
['Country', 'Value', {role: 'tooltip', p:{html:true}}],
['Russia', 5, 'Hello world<br>test'],
['US', 20, '<img src="https://www.google.com/images/srpr/logo6w.png"/>']]);
var chart = new google.visualization.GeoChart(document.getElementById('visualization'));
chart.draw(data, {
width: 800,
height: 600,
tooltip: {
isHtml: true
}
});
}
Since v1.1 geochart supports html for tooltips
There are three alternatives presented in this thread
Set the formatted value of your data points manually (using the #setFormattedValue() DataTable method)
Use one of the formatters on a DataTable column
Include a 'tooltip' role column in the DataTable
I've personally used first one, and with your example should be like
var data = google.visualization.arrayToDataTable([
[ 'State', 'Relevance' ],
[ 'Alabama', { v: 3, f: 'tooltip test text' } ],
[ 'Arizona', { v: 1, f: 'tooltip test text' } ],
]);
I ran into a similar issue for a scatter chart. I had to use arrayToDataTable to get the data into the chart as opposed to DataTable() and addColumn() as suggested in other answers.
In order to get it to work, you make the call to arrrayToDataTable() as you are currently doing and saving to the variable data.
Then, you need to specify which column you want to be treated as a tooltip (and you must specify which columns should be plotted as well). In the following example, columns 0 and 1 contain the plot data, and column 2 contains the tooltip strings.
var view = new google.visualization.DataView(data);
view.setColumns([0, 1, {sourceColumn: 2,role:'tooltip'}]);
Finally, you must make the call to draw with the view variable rather than with the data variable:
chart.draw(view, options);
It seems like it is impossible to format the text the exact way I was attempting to with the GeoChart tool. Below is the solution I finally came up with and the rendered map:
Rendered Map with Tooltip View
PHP & JavaScript Code
<!-- generate geo map -->
<script type='text/javascript' src='https://www.google.com/jsapi'></script>
<script type='text/javascript'>
google.load( 'visualization', '1', { 'packages': ['geochart'] } );
google.setOnLoadCallback( drawRegionsMap );
function drawRegionsMap()
{
// create data table
var data = google.visualization.arrayToDataTable([
<?php echo $data_table; ?>
]);
// create chart object
var chart = new google.visualization.GeoChart(
document.getElementById( 'chart_div' )
);
// defines the data for the tooltip
var formatter = new google.visualization.PatternFormat('{0}');
formatter.format( data, [0,1], 1 );
var formatter = new google.visualization.PatternFormat('{2}');
formatter.format( data, [0,1,2], 0 );
// defines the data for the chart values
var view = new google.visualization.DataView(data);
view.setColumns([0, 1]);
// set options for this chart
var options =
{
width: <?php echo $width; ?>,
region: 'US',
resolution: 'provinces',
colorAxis: { colors: ['#abdfab', '#006600'] },
legend: 'none'
};
// draw chart
chart.draw( view, options );
};
</script>
<div id="chart_div" style="width: <?php echo $width; ?>px; height: <?php echo $height; ?>px;"></div>
Rendered HTML
<script type='text/javascript' src='https://www.google.com/jsapi'></script>
<script type='text/javascript'>
google.load( 'visualization', '1', { 'packages': ['geochart'] } );
google.setOnLoadCallback( drawRegionsMap );
function drawRegionsMap()
{
// create data table
var data = google.visualization.arrayToDataTable([
[ 'State', 'in', 'String' ],
[ 'Arizona', 2, 'Has Facility, Sells Direct' ],
[ 'California', 4, 'Has Facility, Has Distributor, Sells Direct' ],
[ 'Colorado', 1, 'Sells Direct' ],
[ 'Florida', 1, 'Has Distributor' ],
[ 'Georgia', 1, 'Has Distributor' ],
[ 'Idaho', 1, 'Sells Direct' ],
[ 'Illinois', 1, 'Has Distributor' ],
[ 'Indiana', 1, 'Has Distributor' ],
[ 'Iowa', 1, 'Has Distributor' ],
[ 'Kansas', 1, 'Has Distributor' ],
[ 'Kentucky', 1, 'Has Distributor' ],
[ 'Louisiana', 1, 'Has Distributor' ],
[ 'Maryland', 2, 'Has Distributor' ],
[ 'Montana', 1, 'Sells Direct' ],
[ 'Nevada', 2, 'Has Facility, Sells Direct' ],
[ 'New Mexico', 2, 'Has Facility, Sells Direct' ],
[ 'North Carolina', 1, 'Has Distributor' ],
[ 'North Dakota', 1, 'Has Distributor' ],
[ 'Oklahoma', 1, 'Sells Direct' ],
[ 'Oregon', 1, 'Sells Direct' ],
[ 'Pennsylvania', 1, 'Has Distributor' ],
[ 'South Carolina', 1, 'Has Distributor' ],
[ 'Tennessee', 1, 'Has Distributor' ],
[ 'Texas', 1, 'Has Distributor' ],
[ 'Utah', 2, 'Has Facility, Sells Direct' ],
[ 'Washington', 1, 'Sells Direct' ],
[ 'Wyoming', 1, 'Sells Direct' ], ]);
// create chart object
var chart = new google.visualization.GeoChart(
document.getElementById( 'chart_div' )
);
// defines the data for the tooltip
var formatter = new google.visualization.PatternFormat('{0}');
formatter.format( data, [0,1], 1 );
var formatter = new google.visualization.PatternFormat('{2}');
formatter.format( data, [0,1,2], 0 );
// defines the data for the chart values
var view = new google.visualization.DataView(data);
view.setColumns([0, 1]);
// set options for this chart
var options =
{
width: 286,
region: 'US',
resolution: 'provinces',
colorAxis: { colors: ['#abdfab', '#006600'] },
legend: 'none'
};
// draw chart
chart.draw( view, options );
};
</script>
<div id="chart_div" style="width: 286px; height: 180px;"></div>