Group an array of objects in AngularJS - javascript

Based on an array looking like this:
var members = [
{name: "john", team: 1},
{name: "kevin", team: 1},
{name: "rob", team: 2},
{name: "matt", team: 2},
{name: "clint", team: 3},
{name: "will", team: 3}
];
I need to create an unordered list for each team.
Can it be done directly with ngRepeat and a filter maybe?
Or is it easier to reorganize the array into an array of teams with a list of members?
var teams = [
{id: 1, members: ["john", "kevin"]},
{id: 2, members: ["rob", "matt"]},
{id: 3, members: ["clint", "will"]}
]
The nested ngRepeat would be easy to implement, but how do I go from the first array to this one in a simple / clever manner?
Note: The array doesn't come from the database but from an html table. So it's just a flat list of members.
function MyController() {
this.members = [
{name: "john", team: 1},
{name: "kevin", team: 1},
{name: "rob", team: 2},
{name: "matt", team: 2},
{name: "clint", team: 3},
{name: "will", team: 3}
];
}
angular.module('app', []);
angular.module('app')
.controller('MyController', MyController);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.3/angular.min.js"></script>
<div ng-app="app">
<div ng-controller="MyController as ctrl">
<ul>
<li ng-repeat="member in ctrl.members">{{ member.name }} - {{ member.team }}</li>
</ul>
</div>
</div>

You have to group items. For this, I used reduce method in order to create
a hash collection which looks like this:
{
"1": [
"john",
"kevin"
],
"2": [
"rob",
"matt"
],
"3": [
"clint",
"will"
]
}
The reduce() method applies a function against an accumulator and each
element in the array (from left to right) to reduce it to a single
value.
The following step is to map this collection in an array. For this, you should use map method.
The map() method creates a new array with the results of calling a
provided function on every element in this array.
var members = [
{name: "john", team: 1},
{name: "kevin", team: 1},
{name: "rob", team: 2},
{name: "matt", team: 2},
{name: "clint", team: 3},
{name: "will", team: 3}
];
var groups = members.reduce(function(obj,item){
obj[item.team] = obj[item.team] || [];
obj[item.team].push(item.name);
return obj;
}, {});
var myArray = Object.keys(groups).map(function(key){
return {team: key, name: groups[key]};
});
console.log(myArray);

Expanding over the #Alexandru-Ionut Mihai's answer, (just to experience how powerful a reduce function is).. here's a single reduce without having to map it again to achieve the same thing. :)
var members = [
{name: "john", team: 1},
{name: "kevin", team: 1},
{name: "rob", team: 2},
{name: "matt", team: 2},
{name: "clint", team: 3},
{name: "will", team: 3}
];
/*var teams = [
{id: 1, members: ["john", "kevin"]},
{id: 2, members: ["rob", "matt"]},
{id: 3, members: ["clint", "will"]}
]*/
var group_to_values = members.reduce(function(arr, item){
arr[item.team - 1] = arr[item.team - 1] || { id: item.team, members: []};
arr[item.team - 1].members.push(item.name);
return arr;
}, []);
console.log(group_to_values);

If you are using underscore.js, then you can use _.groupBy() method to group array of records on the basis of team.
function MyController() {
this.members = [
{name: "john", team: 1},
{name: "kevin", team: 1},
{name: "rob", team: 2},
{name: "matt", team: 2},
{name: "clint", team: 3},
{name: "will", team: 3}
];
var grouped = _.groupBy(this.members, function(member) {
return member.team;
});
}

You can use a groupBy filter like this
<ul ng-repeat="(key, value) in members | orderBy: 'team' | groupBy:ourGrouper">
<li>{{key}}
<ul>
<li ng-repeat="mem in value | orderBy: 'name'">
{{mem.name}}
</li>
</ul>
</li>
</ul>
See this FIDDLE

var members = [
{name: "john", team: 1},
{name: "kevin", team: 1},
{name: "rob", team: 2},
{name: "matt", team: 2},
{name: "clint", team: 3},
{name: "will", team: 3}
];
var groups = members.reduce(function(obj,item){
obj[item.team] = obj[item.team] || [];
obj[item.team].push(item.name);
return obj;
}, {});
var myArray = Object.keys(groups).map(function(key){
return {team: key, name: groups[key]};
});
console.log(myArray);

Related

How to only filter an item of each type?

Given an array of objects such as
[
{name: "john", power: 1},
{name: "john", power: 129391},
{name: "john", power: 91},
{name: "philip", power: 33},
{name: "philip", power: 883},
{name: "philip", power: 3112333},
]
I'd like to get only one item of each name - the item with the most power. The result would be:
{name: "john", power: 129391},
{name: "philip", power: 3112333},
I just don't know how to filter one of each. I am able to filter items with the same name but not with different names
items.filter(item => item.name === 'john')
One thing I thought was to compare the items to the items that already are in the resulting array but haven't found a way to access the resulting array.
Try reduce
const data = [
{name: "john", power: 1},
{name: "john", power: 129391},
{name: "john", power: 91},
{name: "philip", power: 33},
{name: "philip", power: 883},
{name: "philip", power: 3112333},
]
const powerUsersObj = data.reduce((acc,cur) => {
acc[cur.name] = acc[cur.name] ? Math.max(acc[cur.name],cur.power) : cur.power
return acc
},{})
console.log(powerUsersObj)
const powerUsersArr = Object.entries(powerUsersObj).map(entry => ({name:entry[0],power:entry[1]}))
console.log(powerUsersArr)
Traverse the first array and accumulate the unique keys you want to keep in another array. Then traverse again, doing the checks you want on a per unique key basis (in this key comparing power):
let input = [
{ name: "john", power: 1 },
{ name: "john", power: 129391 },
{ name: "john", power: 91 },
{ name: "philip", power: 33 },
{ name: "philip", power: 883 },
{ name: "philip", power: 3112333 },
];
let uniqueKeys = input
.map((e) => e.name)
.filter((key, i) => input.findIndex((e) => e.name === key) === i);
let output = uniqueKeys.map(
(key) =>
input
.filter((e) => e.name === key)
.sort((entryA, entryB) => entryB.power - entryA.power)[0]
);
Use some dictionary or map to keep track of old selections, and replace items as necessary while iterating.
var arr = [
{name: "john", power: 1},
{name: "john", power: 129391},
{name: "john", power: 91},
{name: "philip", power: 33},
{name: "philip", power: 883},
{name: "philip", power: 3112333},
]
var selectedItems = {}
arr.forEach(item => {
const key = item.name;
if(selectedItems[key] && selectedItems[key].power < item.power) {
selectedItems[key] = item
} else if(!selectedItems[key]) {
selectedItems[key] = item
}
});
console.log(Object.values(selectedItems))
You should use reduce function
const table = [
{name: "john", power: 1},
{name: "john", power: 129391},
{name: "john", power: 91},
{name: "philip", power: 33},
{name: "philip", power: 883},
{name: "philip", power: 3112333},
]
const result = [...table.reduce((r, o) => {
const key = o.name;
const item = r.get(key) || Object.assign({ name: o.name }, {power: 0});
item.power = o.power>item.power? o.power: item.power;
return r.set(key, item);
}, new Map).values()]
console.log(result);

Javascript Object Array Sorting Issue

I have a JavaScript array of objects to sort and arrange it as a new object array preserving the order.
My array of objects. I get them ordered in the way that I want. The index of the objects are 0,1 & 2 in this.
[{0: {name: 'Joel', age:25, id: 2}}, {1: {name: 'Sam', age: 23, id: 4}}, {2: {name: 'Tim', age:27, id: 3}}]
What I want is to make 'id' the index value. And I want it in ascending order of the 'name' (Preserving the above order)
[{2: {name: 'Joel', age:25, id: 2}}, {4: {name: 'Sam', age: 23, id: 4}}, {3: {name: 'Tim', age:27, id: 3}}]
I tried using this function.
for (i in members) {
member[members[i].id] = members[i];
}
But it failed. The output was
[{}, {}, {name: "Joel", age:25, id: 2}, {name: "Tim", age:27, id: 3}, {name: "Sam", age: 23, id: 4}]
I tried using forEach and sort methods too. But all failed.
Is there any way to accomplish this in JavaScript.
You can do it with help of map and sort.
let obj = [{0: {name: 'Joel', age:25, id: 2}}, {1: {name: 'Sam', age: 23, id: 4}}, {2: {name: 'Tim', age:27, id: 3}}]
let op = obj.map((e,index)=>{
return{
[e[index].id] : e[index]
}
}).sort((a,b)=> Object.keys(a) - Object.keys(b))
console.log(op)

How to output an array in Javascript like a social media feed?

I'm using React for my application and I've put my data (objects) into an array. Is there a way to output my data (array of objects) that loads all at once (such as Twitter, Instagram, Facebook)?
Currently I'm using a for loop where it loads one by one from my latest post to the end.
Here's a sample for loop to demonstrate.
var myArray = [
{name: 'Dwayne', age: 28},
{name: 'Rob', age: 32},
{name: 'Marie', age: 22},
{name: 'Sarah', age: 40},
{name: 'Emma', age: 29},
{name: 'James', age: 30}
];
for (var i = myArray.length - 1; i >= 0; i--){
console.log(myArray[i].name, myArray[i].age);
}
Here's an example of using map to generate a <p/> element with name and age inside it.
render(){
const myArray = [
{name: 'Dwayne', age: 28},
{name: 'Rob', age: 32},
{name: 'Marie', age: 22},
{name: 'Sarah', age: 40},
{name: 'Emma', age: 29},
{name: 'James', age: 30}
];
return(
<div>
{myArray.map((item, index) => (
<p key={`${item.name}-${index}`}>
Name:{item.name}, Age:{item.age}
</p>
))}
</div>
)
}
The above code would output
<div>
<p>Name:Dwayne, Age:28</p>
<p>Name:Rob, Age:32</p>
<p>Name:Marie, Age:22</p>
<p>Name:Sarah, Age:40</p>
<p>Name:Emma, Age:29</p>
<p>Name:James, Age:30</p>
</div>

Lodash, taking objects fulfilling requirements

Example:
Animals = [{Name: 'Dog', Id: 0},
{Name: 'Cat', Id: 1},
{Name: 'Mouse', Id: null}]
How to take all objects where Id isn't null into new array?
Expected output:
NewArray = [{Name: 'Dog', Id: 0},
{Name: 'Cat', Id: 1}]
Try with _.filter
var Animals = [{Name: 'Dog', Id: 0},{Name: 'Cat', Id: 1},{Name: 'Mouse', Id:null}]
var newArray =_.filter(Animals ,a=> a.Id != null)
console.log(newArray)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.11.2/lodash.min.js"></script>

How to populate an array in enyo.js?

I have an array. I want to give the value of the array in enyo.js.
How will I populate the array dynamically?
Following is the code:
enyo.kind({
name: "enyo.sample.RepeaterSample",
classes: "enyo-fit repeater-sample",
components: [
{kind: "Repeater", onSetupItem:"setupItem", components: [
{name:"item", classes:"repeater-sample-item", components: [
{tag:"span", name: "personNumber"},
{tag:"span", name: "personName"}
]}
]}
],
create: function() {
this.inherited(arguments);
this.$.repeater.setCount(this.people.length);
},
setupItem: function(inSender, inEvent) {
var index = inEvent.index;
var item = inEvent.item;
var person = this.people[index];
item.$.personNumber.setContent((index+1) + ". ");
item.$.personName.setContent(person.name);
item.$.personName.applyStyle("color", person.sex == "male" ? "dodgerblue" : "deeppink");
},
people: [
{name: "Andrew", sex:"male"},
{name: "Betty", sex:"female"},
{name: "Christopher", sex:"male"},
{name: "Donna", sex:"female"},
{name: "Ephraim", sex:"male"},
{name: "Frankie", sex:"male"},
{name: "Gerald", sex:"male"},
{name: "Heather", sex:"female"},
{name: "Ingred", sex:"female"},
{name: "Jack", sex:"male"},
{name: "Kevin", sex:"male"},
{name: "Lucy", sex:"female"},
{name: "Matthew", sex:"male"},
{name: "Noreen", sex:"female"},
{name: "Oscar", sex:"male"},
{name: "Pedro", sex:"male"},
{name: "Quentin", sex:"male"},
{name: "Ralph", sex:"male"},
{name: "Steven", sex:"male"},
{name: "Tracy", sex:"female"},
{name: "Uma", sex:"female"},
{name: "Victor", sex:"male"},
{name: "Wendy", sex:"female"},
{name: "Xin", sex:"male"},
{name: "Yulia", sex:"female"},
{name: "Zoltan"},
]
});
In this case the people is hard coded.
I want to give my value that m getting from json dynamically.
Please help. Thanks.
When you get the array of JSON, then you just set this.people = [data from JSON]. That works if the code is contained in the same top-level kind (enyo.sample.RepeaterSample).
If the JSON return is in a different kind, then you probably need to set a published property for enyo.sample.RepeaterSample thusly:
published: {
people: []
}
And then you can use setPeople(arrayOfPeople). You can also then automatically re-build the repeater in a peopleChanged method:
peopleChanged: function() {
this.$.repeater.setCount(this.people.length);
}

Categories

Resources