Lodash group By - lodash

What I need is to group by project, that for each project I am grouped by the stories that are in those projects, and that for each story I show the tasks. When executing the example, we will see that the history 41 that corresponds to the "barney" project is not shown. It groups the stories but does not show the history corresponding to a barney that is not grouped.
This is an example of how it should look
[
{
"proyecto": "barney",
"historia": 42,
"tarea": [
"dog",
"lion",
"cat"
],
"historia": 41,
"tarea": [
"cat"
]
},
{
"proyecto": "fred",
"historia": 35,
"tarea": [
"dog",
"goldfish"
]
}
]
group();
function group()
{
var characters = [
{ 'proyecto': 'barney', 'historia': 42, 'tarea': 'dog' },
{ 'proyecto': 'barney', 'historia': 42, 'tarea': 'lion' },
{ 'proyecto': 'fred', 'historia': 35, 'tarea': 'dog' },
{ 'proyecto': 'barney', 'historia': 41, 'tarea': 'cat' },
{ 'proyecto': 'fred', 'historia': 35, 'tarea': 'goldfish' }
];
var result=_.chain(characters).groupBy("proyecto").map(function(v, i) {
return {
proyecto: i,
historia: _.get(_.find(v, 'historia'), 'historia'),
tarea: _.map(v, 'tarea')
}
}).value();
document.body.innerHTML = '<pre>' + JSON.stringify(result, null, ' ') + '</pre>';
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>

Since you already have the lodash answer here is the ES6 implementation of this via single Array.reduce just so you have to to compare:
var data = [ { 'proyecto': 'barney', 'historia': 42, 'tarea': 'dog' }, { 'proyecto': 'barney', 'historia': 42, 'tarea': 'lion' }, { 'proyecto': 'fred', 'historia': 35, 'tarea': 'dog' }, { 'proyecto': 'barney', 'historia': 41, 'tarea': 'cat' }, { 'proyecto': 'fred', 'historia': 35, 'tarea': 'goldfish' } ];
const result = data.reduce((r, {proyecto, historia, tarea}) => {
r[proyecto] = r[proyecto] || {proyecto, historia: [], tarea: []}
r[proyecto].historia.push(historia)
r[proyecto].tarea.push(tarea)
return r
}, {})
console.log(Object.values(result))
Since you are using reduce there is no need for the 2nd _.map loop and all can be done in one "shot".
If ES6 is an issue here is the same implementation with _.reduce:
var data = [ { 'proyecto': 'barney', 'historia': 42, 'tarea': 'dog' }, { 'proyecto': 'barney', 'historia': 42, 'tarea': 'lion' }, { 'proyecto': 'fred', 'historia': 35, 'tarea': 'dog' }, { 'proyecto': 'barney', 'historia': 41, 'tarea': 'cat' }, { 'proyecto': 'fred', 'historia': 35, 'tarea': 'goldfish' } ];
const result = _.reduce(data, function(r,c) {
r[c.proyecto] = r[c.proyecto] || {proyecto: c.proyecto, historia: [], tarea: []}
r[c.proyecto].tarea.push(c.tarea)
r[c.proyecto].historia.push(c.historia)
return r
}, {})
console.log(_.values(result))
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>

Use _.map() to extract the historia (like you do for tarea):
var characters = [
{ 'proyecto': 'barney', 'historia': 42, 'tarea': 'dog' },
{ 'proyecto': 'barney', 'historia': 42, 'tarea': 'lion' },
{ 'proyecto': 'fred', 'historia': 35, 'tarea': 'dog' },
{ 'proyecto': 'barney', 'historia': 41, 'tarea': 'cat' },
{ 'proyecto': 'fred', 'historia': 35, 'tarea': 'goldfish' }
];
var result= _(characters)
.groupBy("proyecto")
.map(function(v, i) {
return {
proyecto: i,
historia: _.map(v, 'historia'),
tarea: _.map(v, 'tarea')
}
})
.value();
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>

Related

Put array in chart data

I use vue3-chart-v2 in my Vue3 app.
I would like to put array instead of predefined numbers.
Can anybody help me?
This is my chart component:
<script>
import { defineComponent } from 'vue'
import { Line } from 'vue3-chart-v2'
export default defineComponent({
name: 'ChanceChart',
extends: Line,
props: {
chartData: {
type: Object,
required: true
},
chartOptions: {
type: Object,
required: false
},
},
mounted () {
this.renderChart(this.chartData, this.chartOptions)
}
})
</script>
And this is how it looks like in the app:
<template>
<Navigation />
<div>
<ChanceChart :chartData="state.chartData"/>
</div>
</template>
<script>
import ChanceChart from '../components/ChanceChart.vue';
export default {
components: {
ChanceChart
},
data() {
return {
enemysCards : [1610, 169, 168, 167, 330, 329, 328, 327, 326, 325, 324, 323, 1, 1610, 1600, 169, 168, 167, 1610, 1600, 169, 168, 167, 11],
state: {
chartData: {
datasets: [
{
label: "Enemy's Chance",
borderColor: '#1161ed',
data: [40, 20, 12, 39, 10, 40, 39, 80, 40, 20, 12, 41, 190]
},
{
label: 'My Chance',
borderColor: '#f87979',
color: '#fff',
data: [60,60,60,60,60,60,60,60,60,60,60,60, 60]
}
],
labels: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13'],
},
chartOptions: {
responsive: true
}
}
}
}
For example i want to take the enemysCards array in the data and the enemysCards.lenght in the labels.
Use computed properties instead of data property :
export default {
components: {
ChanceChart
},
computed: {
chartData(){
return {
datasets: [
{
label: "Enemy's Chance",
borderColor: '#1161ed',
data: this.myFirstArray // <- here
},
{
label: 'My Chance',
borderColor: '#f87979',
color: '#fff',
data: this.mySecondArray // <- here
}
],
labels: this.ennemyCards.map((x, index) => index + 1) // here you can calculate your labels
},
chartOptions: {
responsive: true
}
}
}
data() {
return {
enemysCards : [1610, 169, 168, 167, 330, 329, 328, 327, 326, 325, 324, 323, 1, 1610, 1600, 169, 168, 167, 1610, 1600, 169, 168, 167, 11],
myFirstArray: [40, 20, 12, 39, 10, 40, 39, 80, 40, 20, 12, 41, 190],
mySecondArray: [40, 20, 12, 39, 10, 40, 39, 80, 40, 20, 12, 41, 190],
}
}

How to count the value in an nested array in Vue.js?

I'm trying to count how many hr_checked = false by specific user on a nested array inside an array in an Vue.js. Here's a snippet of array code:
userlistes: [
{
id: 2,
username: "Larry",
department_id: 3,
department: {
department_name: "IT",
id: 3,
},
worklists: [
{
id: 278,
user_id: 2,
task_id: 1,
date: "2021-07-30",
hour: 2,
description: "A",
is_overtime: false,
overtime_hour: 0,
task: {
taskname: "Task A",
},
hr_checked: false,
},
{
id: 277,
user_id: 2,
task_id: 1,
date: "2021-07-30",
hour: 3,
description: "B",
is_overtime: false,
overtime_hour: 0,
task: {
taskname: "Task B",
},
hr_checked: false,
},
],
},
{
id: 4,
username: "Tom",
department_id: 2,
department: {
department_name: "Business",
id: 2,
},
worklists: [
{
id: 259,
user_id: 4,
task_id: 7,
date: "2021-07-27",
hour: 6.5,
description:
"A",
is_overtime: false,
overtime_hour: 0,
task: {
taskname: "Task A",
},
hr_checked: false,
},
{
id: 260,
user_id: 4,
task_id: 7,
date: "2021-07-27",
hour: 0.5,
description: "B",
is_overtime: false,
overtime_hour: 0,
task: {
taskname: "Task B",
},
hr_checked: false,
},
],
},
],
And i tried to used Vue computed property to implement this:
computed: {
countCheck() {
return this.userlistes.filter((userliste) => {
return userliste.workhours.reduce((sum, workhour) => {
if (workhour.hr_checked === false) {
sum++;
}
return sum;
}, 0);
});
}
I want to get the count of the values inside the nested array's 'hr_checked'.
the returning result should be like:
Larry: unchecked 2
Tom: unchecked 2
Is there any way to do this in Vue.js? or i'm use wrong function??
Try to map the wrapping array then reduce the nested one :
return this.userlistes.map((item)=>({username:item.username,
unchecked :item.worklists.reduce((sum, workhour) => {
if (workhour.hr_checked === false) {
sum++;
}
return sum;
}, 0)}))
let userlistes = [{
id: 2,
username: "Larry",
department_id: 3,
department: {
department_name: "IT",
id: 3,
},
worklists: [{
id: 278,
user_id: 2,
task_id: 1,
date: "2021-07-30",
hour: 2,
description: "A",
is_overtime: false,
overtime_hour: 0,
task: {
taskname: "Task A",
},
hr_checked: false,
},
{
id: 277,
user_id: 2,
task_id: 1,
date: "2021-07-30",
hour: 3,
description: "B",
is_overtime: false,
overtime_hour: 0,
task: {
taskname: "Task B",
},
hr_checked: false,
},
],
},
{
id: 4,
username: "Tom",
department_id: 2,
department: {
department_name: "Business",
id: 2,
},
worklists: [{
id: 259,
user_id: 4,
task_id: 7,
date: "2021-07-27",
hour: 6.5,
description: "A",
is_overtime: false,
overtime_hour: 0,
task: {
taskname: "Task A",
},
hr_checked: false,
},
{
id: 260,
user_id: 4,
task_id: 7,
date: "2021-07-27",
hour: 0.5,
description: "B",
is_overtime: false,
overtime_hour: 0,
task: {
taskname: "Task B",
},
hr_checked: false,
},
],
},
]
let mapped = userlistes.map((item) => ({
username: item.username,
unchecked: item.worklists.reduce((sum, workhour) => {
if (workhour.hr_checked === false) {
sum++;
}
return sum;
}, 0)
}))
console.log(mapped)
Here is another solution:
link to codesandbox example
Code in computed props:
computed: {
getHRByUser() {
let res = [];
let reducer = (sum, workitem) => {
if (workitem.hr_checked === false) {
sum++;
}
return sum;
};
for (const i in this.userlistes) {
let inactiveHRCount = this.userlistes[i].worklists.reduce(reducer, 0);
res.push({
username: this.userlistes[i].username,
inactiveHRCount: inactiveHRCount,
});
}
return res;
},
}
But Boussadjra Brahim proposed a bit more elegant solution in this case.
computed is fine as your data are dynamic and the function would run everytime the data changes. I would use the above example
countCheck() {
let count = 0
this.userlistes.filter((userliste) => {
if (userliste.workhours && userliste.workhours.hr_checked) {
count++
}
})
return count
}

How to get the grouped data with true conditions only

How to get the grouped data with true conditions only. Below will return both values with key true and false.
var users = [
{ 'user': 'barney', 'age': 36, 'active': true },
{ 'user': 'fred', 'age': 40, 'active': false },
{ 'user': 'barney', 'age': 36, 'active': true },
{ 'user': 'barney', 'age': 36, 'active': true },
{ 'user': 'barney', 'age': 36, 'active': true }
];
result = _.groupBy(users, { 'user': 'fred' });
You need to filter the data before you group:
var result = _(users)
.filter('active')
.groupBy('user')
.value();

Flatten an nested array of objects using Lodash

My object looks like this:
const features = [{
'name': 'feature1', 'tags':
[{'weight':10, 'tagName': 't1'},{'weight':20, 'tagName': 't2'}, {'weight':30, 'tagName': 't3'}]
},
{
'name': 'feature2', 'tags':
[{'weight':40, 'tagName': 't1'}, {'weight':5, 'tagName':'t2'}, {'weight':70, 'tagName':'t3'}]
},
{
'name': 'feature3', 'tags':[
{'weight':50, 'tagName': 't1'}, {'weight':2, 'tagName': 't2'}, {'weight':80, 'tagName': 't3'}]
}]
I would like my output to look something like this:
const features = [{'name':'feature1', 'weight':10, 'tagName':'t1'},
{'name':'feature1', 'weight':20, 'tagName':'t2'}, ...
{'name':'feature3', 'weight':80, 'tagName':'t3'}]
I tried to merge and the flatten but it does not work.
Update 1
I tried this:
let feat = features;
results = []
_.each(feat, (item) => {
console.log(item);
results.push(_.flatten(_.pick(item.tags, 'weight'))); // pick for certain keys.
}
Update 2
This solved my problem
_.each(features, (item) => {
_.each(item.tags, (itemTag) => {
results.push({'name':item.name, 'weight':itemTag.weight, 'tagName':itemTag.tagName})})})
But I want to know if there is a more lodash way to do this!
The approach below uses flatMap to flatten tags acquired through map. Finally, use the spread operator to assign the values from tag and the feature's name.
const result = _.flatMap(features, ({ name, tags }) =>
_.map(tags, tag => ({ name, ...tag }))
);
const features = [{
'name': 'feature1',
'tags': [{
'weight': 10,
'tagName': 't1'
}, {
'weight': 20,
'tagName': 't2'
}, {
'weight': 30,
'tagName': 't3'
}]
},
{
'name': 'feature2',
'tags': [{
'weight': 40,
'tagName': 't1'
}, {
'weight': 5,
'tagName': 't2'
}, {
'weight': 70,
'tagName': 't3'
}]
},
{
'name': 'feature3',
'tags': [{
'weight': 50,
'tagName': 't1'
}, {
'weight': 2,
'tagName': 't2'
}, {
'weight': 80,
'tagName': 't3'
}]
}
];
const result = _.flatMap(features, ({ name, tags }) =>
_.map(tags, tag => ({ name, ...tag }))
);
console.log(result);
.as-console-wrapper{min-height:100%;top:0}
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>
Here's a plain javascript solution that uses Array#reduce and Array#map with the help of Array#concat to flatten the array.
const result = features.reduce(
(result, { name, tags }) => result
.concat(tags.map(tag => ({ name, ...tag }))),
[]
);
const features = [{
'name': 'feature1',
'tags': [{
'weight': 10,
'tagName': 't1'
}, {
'weight': 20,
'tagName': 't2'
}, {
'weight': 30,
'tagName': 't3'
}]
},
{
'name': 'feature2',
'tags': [{
'weight': 40,
'tagName': 't1'
}, {
'weight': 5,
'tagName': 't2'
}, {
'weight': 70,
'tagName': 't3'
}]
},
{
'name': 'feature3',
'tags': [{
'weight': 50,
'tagName': 't1'
}, {
'weight': 2,
'tagName': 't2'
}, {
'weight': 80,
'tagName': 't3'
}]
}
];
const result = features.reduce(
(result, { name, tags }) => result
.concat(tags.map(tag => ({ name, ...tag }))),
[]
);
console.log(result);
.as-console-wrapper{min-height:100%;top:0}
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>

How to map hierarchical Json to ItemFileWriteStore?

I have Json data that has children elements. I need to bind the store to an editable grid and have the edits populated to the store.
The data tree does get populated into the ItemFileWriteStore. The datagrid displays only the parent data and none of the children data.
SAMPLE.TXT
{
"items": [
{
"profileId": "1",
"profileName": "ABC",
"profileType": "EmailProfile",
"profilePreferences": [
{
"profilePreferenceId": "1",
"displayText": "Bob",
"address": "primary#some.com"
},
{
"profilePreferenceId": "2",
"displayText": "Sally",
"address": "secondary#some.com"
},
{
"profilePreferenceId": "3",
"displayText": "Joe",
"address": "alternate#some.com"
}
]
}
]
}
javascript
var sampleLayout = [
[
{ field: 'profileName', name: 'profileName', width: '100px' },
{ field: 'profilePreferences.displayText', name: 'displayText', width: '100px' },
{ field: 'profilePreferences.address', name: 'address', width: '100px' }
]];
function populateGrid() {
var url = "sample.txt"; //Will be replaced with endpoint URL
dojo.xhrGet({
handleAs: 'json',
url: url,
error: function (e) {
alert("Error: " + e.message);
},
load: showJsonData
});
}
function showJsonData(response, ioArgs) {
var profileStore = new dojo.data.ItemFileWriteStore({
data: {
items: response.items
}
});
var sampleGrid = dijit.byId("sampleGrid");
sampleGrid.store = profileStore;
sampleGrid.startup();
}
you need to be using dojox.grid.TreeGrid or 'fake' the JSON to present every even row with a blank profileName. Two samples follows, one for TreeGrid another on DataGrid - not tested in working environment though.
Given Hierachial JSON:
{
identifier: 'id' // a good custom to make an id pr item, note spaces and odd chars are invalid
items: [{
id: '1',
profileName: 'Admin',
profilePreferences: [
{ id: '1_1', displayText: 'John Doe', address: 'Big Apple' }
{ id: '1_2', displayText: 'Jane Doe', address: 'Hollywood' }
]
}, {
id: '2',
profileName: 'Visitor',
profilePreferences: [
{ id: '2_1', displayText: 'Foo', address: 'Texas' }
{ id: '2_2', displayText: 'Bar', address: 'Indiana' }
]
}]
}
TreeGrid Structure:
{
cells: [
[
{ field: "profileName", name: "profileName", width: "100px" },
{ field: "profilePreferences",
children: [
{ field: "displayText" name: "displayText", width: "100px" },
{ field: "address" name: "address", width: "100px" }
]
]
]
}
reference: dojo docs
Given flattened 'fake-children' JSON:
{
identifier: 'id' // a good custom to make an id pr item, note spaces and odd chars are invalid
items: [{
id: '1',
profileName: 'Admin', preferenceText: '', preferenceAddr: ''
}, {
id: '2',
profileName: '', preferenceText: 'John', preferenceAddr: 'NY'
}, {
id: '3',
profileName: 'Visitor', preferenceText: '', preferenceAddr: ''
}, {
id: '4', // Not with '.' dot seperator like so
profileName: '', preference.Text: 'Jane Doe', preference.Addr: 'Hollywood'
} ]
DataGrid structure:
[[
{'name': 'Profilename', 'field': 'profileName', 'width': '100px'},
{'name': 'User name', 'field': 'preferenceText', 'width': '100px'},
{'name': 'Address', 'field': 'preferenceAddr', 'width': '200px'}
]]
reference dojo docs