Group by on a complex object in AngularJS - angularjs

I've an array that contains assignments of employees on tasks, it looks like something like this:
$scope.assignments = [
{
employee: {
id:"1", firstname:"John", lastname:"Rambo"
},
task: {
name:"Kill everyone", project:"Destruction"
},
date: {
day:"01/01", year:"1985"
}
},
{
employee: {
id:"2", firstname:"Luke", lastname:"Skywalker"
},
task: {
name:"Find daddy", project:"Star Wars"
},
date: {
day:"65/45", year:"1000000"
}
},
{
employee: {
id:"1", firstname:"John", lastname:"Rambo"
},
task: {
name:"Save the world", project:"Destruction"
},
date: {
day:"02/01", year:"1985"
}
}
];
I would like to group by employee, for having something like this:
$scope.assignmentsByEmployee = [
{ //First item
id:"1",
firstname:"John",
lastname:"Rambo",
missions: [
{
name:"Kill everyone",
date:"01/01",
year:"1985"
},
{
name:"Save the world",
date:"02/01",
year:"1985"
}
]
},
{ //Second item
id="2",
firstname:"Luke",
lastname:"Skywalker",
missions: [
name:"Find daddy",
date:"65/45",
year:"1000000"
]
}
];
Is their a simple way to do this ? I tried something with a double forEach, but it leads me nowhere.
Hope I'm understandable :)
Thanks !

You should just be able to loop through the assignments array and create a 'keyed array' (which just means using an object in JavaScript) on employee ID. Then you just fill up the missions array as required.
Something like
// initialise a holding object
var assignmentsByEmployee = {};
// loop through all assignemnts
for(var i = 0; i < $scope.assignments.length; i++) {
// grab current assignment
var currentAssignment = $scope.assignments[i];
// grab current id
var currentId = currentAssignment.employee.id;
// check if we have seen this employee before
if(assignmentsByEmployee[currentId] === undefined) {
// we haven't, so add a new object to the array
assignmentsByEmployee[currentId] = {
id: currentId,
firstname: currentAssignment.employee.firstname,
lastname: currentAssignment.employee.lastname,
missions: []
};
}
// we know the employee exists at this point, so simply add the mission details
assignmentsByEmployee[currentId].missions.push({
name: currentAssignment.task.name,
date: currentAssignment.date.day,
year: currentAssignment.date.year
});
}
These leaves assignmentsByEmployee as an object, but you can simply foreach through it and convert it back to an array if required. E.g:
$scope.assignmentsByEmployee = [];
for(var employeeId in assignmentsByEmployee) {
$scope.assignmentsByEmployee.push(assignmentsByEmployee[employeeId]);
}

Related

Insert list data over the iteration(map)

Here I am trying to modify my data over the iteration and send some result to API call.
The API Call receives a request with a structured data format which is
{ list: [{ id: "1", name: "Hello" }, ... ] }
Somehow I managed to call the API with single data ( const params in my current code, it only accepts single data).
But now it has to be done with multiple data something like this:
{ list: [{ id: "1", name: "Hello" }, { id: "22", name: "Ed" }, { id: "36", name: "Jason" } ... ] }
Here is my current code
const [table, setTalbe] = useState(..); // assume, we have some table data here
const processNow = () => {
let id = 0;
let name = '';
// if table length is greater than 1, we go for the loop.
if (table.length >= 1) {
table.map(data => {
id = data.userId;
name = data.userName;
});
//insert table data to params, here I want to add whole table data into "list"
//the final result of this list should be something like this
//ex ) list: [{ id: '123', name: 'Josh' }, { id: '125', name: 'Sue' }, { id: '2222', name: 'Paker' } ...],
// but how??
const params: any = {
list: [
{
id: id,
name: name
},
],
};
//send PUT reqeust with params
axios
.put(
'/api/v1/tosent',
params,
)
.then(res => {
console.log('The response', res);
})
.catch(err => {
console.log('The error: ', err);
});
}
};
but I'm stuck with it, please help me to finish this code to work properly.
need your kind advice.
Array.prototype.map returns a new array with the function you pass applied to every element. You should study the MDN documentation on map to understand its use.
Your current code does nothing with the map return value:
table.map(data => {
id = data.userId;
name = data.userName;
});
You probably assumed .map would mutate the data, as in change it in place. Instead, the whole operation returns a new array.
It looks like you want to do:
const list = table.map(data => {
return {
id: data.userId,
name: data.userName
}
});
This is applying a function to every element in the array that will map each element to a new object, matching your question, with an id and name key. Then it looks like you want to pass the returned value of map (which we named list above) to your call:
const params: any = {
list: list
};

ES6 : Create objects in a given format from array of key values

var result = {445: "L005.0", 455: "L006.0", 456: "L007.0", 457: "L008.0", 458: "L009.0", 459: "L027.0", 467: "L005.7", 580: "L001.0", 581: "L002.0", 587: "L003.0"};
From this "result", I want to output an object like this
{
"445": {
name: result[445],
icon: "fa-search"
},
"455": {
name: result[455],
icon: "fa-search"
},
"456": { ... },
"457": { ... },
...
...
}
So you need to iterate over keys to construct new object o;
let res={}; //initializing
for(let i of Object.keys(result))
{
res[i]={
"name": result[i],
"icon":"fa-seach"
}
}
console.log(res) //to test

How to query only objects containing each element from the array given as a variable using Hasura?

I have a table 'Students' in my db(postgress with Hasura) with relation many-to-many with 'Subjects':
type Student = {
id: uuid
name: String
subjects: [subject]
}
type Subject = {
id: uuid
name: String
}
I have a Static QUERY1:
query FilteredStudents($subjects: [String!]) {
students(where: { subjects: { name: { _in: $subjects } } }) {
id
name
}
}
e.g.:
$subjects = ['Math', 'English', 'Physics']
It will find all the students that attend to ANY of those classes.
e.g.:
const student1 = {
id: 1,
name: 'Mike',
subjects: ['Physics', 'Chemistry'] // subjects mapped to names for simplicity
}
My problem is that I want another query which will find all the students that attend to EACH of those classes.
So it shouldn't fetch student1, but should fetch students like this:
const student2 = {
id: 2,
name: 'Walt',
subjects: ['Math', 'English', 'Physics', 'Some', 'other', 'subjects'] // subjects mapped to names for simplicity
}
My only idea was to create dynamic queries like this:
Dynamic QUERY2 (a new query is generated (on runtime) every time the $subject array changes):
query FilteredStudents {
students(where: { _and: [
subjects: { name: { _eq: "Math" } }
subjects: { name: { _eq: "English" } }
subjects: { name: { _eq: "Physics" } }
]}) {
id
name
}
}
But I would really want to avoid that and find some static solution. Is there a way to achieve this using filters offered by Hasura? (https://hasura.io/docs/1.0/graphql/manual/queries/query-filters.html#)
I can name your QUERY2 as static already and QUERY1 as dynamic - 1st is parametrized, 2nd is hardcoded.
You can construct and pass as variable entire where object for both.
You can read this and use _not with _nin to do the same (as '_and' usage). It stil will be a dynamic condition because where is an input type ... and almost the same condition object creation complexity.
update
const subset = ['Math', 'English'];
const condition = { _and: [] };
subset.map( el => {
condition._and.push( {
subjects: { name: { _eq: el } }
});
});
callSomeLazyQuery( { variables: {
where: condition
} );
the same should be valid
const condition = {
_not: {
_and: []
}
};
subset.map( el => {
condition._not._and.push( {
subjects: { name: { _nil: el } }
});
});

using angular.forEach in filter

I have an array (1) with some id's and another array (2) with creatures, they have id (like in first array) and names. And I want to create new array (it will be looks like (1) id array, but only with id that in (2)). So I think that I need use filter.
(1)
$scope.main = ['dog', 'cat', 'bird', 'bug', 'human'];
(2)
$scope.creatures = [
{
id: 'cat',
name : 'fluffy'
},
{
id: 'cat',
name : 'mr.Kitty'
},
{
id: 'human',
name: 'Rachel'
},
{
id: 'cat',
name : 'Lucky'
},
{
id: 'cat',
name: 'Tom'
}
];
filter:
$scope.results = $scope.main.filter(function(item) {
angular.forEach($scope.creatures, function(creature) {
return item === creature.id;
});
});
I expect that it will be
$scope.results === ['cat', 'human'];
But I have
$scope.results // [0] empty array
Where I'm wrong? Plnkr example
It is not working because you are returning in the first iteration itself inside forEach loop. You can get it working as shown below :
Updated Plunker
$scope.results = [];
$scope.main.filter(function(item) {
angular.forEach($scope.creatures, function(creature) {
if(item === creature.id){
if( $scope.results.indexOf(item) === -1){
$scope.results.push(item);
}
}
});
});
Instead of looping again inside the filter, we can get the ids out of creatures array first and then filter them in main array like below :
$scope.results = [];
$scope.ids = $scope.creatures.map(function (creature){
return creature.id;
});
$scope.ids.map(function (id){
if($scope.main.indexOf(id) !== -1){
if( $scope.results.indexOf(id) === -1){
$scope.results.push(id);
}
}
});
console.log($scope.results);
Made few changes in your plunker, it is working now. Can you check with this code.
var app = angular.module('App', []);
app.controller('Ctrl', function($scope, $timeout){
$scope.main = ['dog', 'cat', 'bird', 'bug', 'human'];
$scope.creatures = [
{
id: 'cat',
name : 'fluffy'
},
{
id: 'cat',
name : 'mr.Kitty'
},
{
id: 'human',
name: 'Rachel'
},
{
id: 'cat',
name : 'Lucky'
},
{
id: 'cat',
name: 'Tom'
}
];
var array = [];
$scope.call = function() {
angular.forEach($scope.main,function(item){
angular.forEach($scope.creatures, function(creature) {
if(item == creature.id){
// console.log('item',item,' creatureId',creature.id);
if(array.indexOf(item) < 0 ){
array.push(item);
}
console.log(array);
}
});
$scope.results = array;
});
};
$scope.call();
console.log('result ',$scope.results);
});

Converting array of objects into nested form

I have an array of objects in this form
nnz=[{
"verb":"has",
"nouns": "employees"
},
{
"verb":"has",
"nouns": "managers"
},
{
"verb":"have",
"nouns": "departments"
}
]
but i want to convert it into this form
[ {
"verb":"has",
"nouns": ["employees", "managers"]
},
{
"verb":"have",
nouns:["departments"]
}
]
and this work has to be done from client side. I am working in node.js. I want to allow user to select multiple nouns against each verb and when user selects nouns against verb it should be saved into an array in the above given form. I am unable to figure it out how can i implement this logic.
Update: we have to check verb too.now it became more tricky.
You can do somethong like this, using some Array built-in functions:
const array = [
{
"verb":"has",
"nouns": "employees"
},
{
"verb":"has",
"nouns": "managers"
},
{
"verb":"has",
"nouns": "departments"
}
]
const aggregated = array.reduce( (arr, el) =>{
let found = arr.find(ll => ll.verb == el.verb);
if(found) found.nouns.push(el.nouns);
else arr.push({verb : el.verb, nouns : [el.nouns] })
return arr;
}, []);
console.log(aggregated);
First create a temporary object that uses the verb value as keys then iterate your existing data pushing nouns to corresponding array in that temporary object.
Then map the temporary object to a new array.
nnz = [{
"verb": "has",
"nouns": "employees"
}, {
"verb": "has",
"nouns": "managers"
}, {
"verb": "has",
"nouns": "departments"
}]
var tmp = {};
nnz.forEach(function(item) {
tmp[item.verb] = tmp[item.verb] || {
verb: item.verb,
nouns: []
};
tmp[item.verb].nouns.push(item.nouns);
});
var results = Object.keys(tmp).map(function(key) {
return tmp[key];
});
console.log(results);
Try these two approaches as per your requirement. I hope it will work as per the expectation .
First Approach :
When you know the property names :
var nnz = [{
"verb":"has",
"nouns": "employees"
},
{
"verb":"has",
"nouns": "managers"
},
{
"verb":"has",
"nouns": "departments"
}
];
var newObj = {};
var nouns = [];
for (var i in nnz) {
nouns.push(nnz[i].nouns);
newObj.verb = nnz[i].verb;
newObj.nouns = nouns;
}
nnz = newObj;
console.log(nnz);
Second Approach :
When you don't know the property names :
var nnz = [{
"verb":"has",
"nouns": "employees"
},
{
"verb":"has",
"nouns": "managers"
},
{
"verb":"has",
"nouns": "departments"
}
];
var newObj = {};
var nounsArr = [];
for (var i in nnz) {
nounsArr.push(nnz[i][Object.keys(nnz[i])[1]])
nnz[i][Object.keys(nnz[i])[1]] = nounsArr;
var Objlen = Object.keys(nnz[i]).length;
for (var j=0;j<Objlen;j++) {
var objKeys = Object.keys(nnz[i])[j];
newObj[objKeys] = nnz[i][objKeys];
}
}
nnz = newObj;
console.log(nnz);

Resources