How to count the number of rows containing a certain value? - javascript

I'm using AngularJS and I have a table that I populate using ng-repeat. Check this short example:
http://jsfiddle.net/sso3ktz4/
How do I check how many rows I have with a certain value? For example, in the fiddle above, how do I check how many rows I have with the word "second"? It should return "3".
AngularJS answers are preferred, although I also have JQuery available.
Thanks!

updated controller code is below, where $scope.findRowCount is required function
var myApp = angular.module('myApp', []).controller('MyCtrl', MyCtrl);
function MyCtrl($scope) {
$scope.items = [{
name: 'first',
examples: [{
name: 'first 1'
}, {
name: 'first 2'
}]
}, {
name: 'second',
examples: [{
name: 'second'
}, {
name: 'second'
}]
}];
$scope.findRowCount=function(value){
var count=0;
angular.forEach($scope.items, function(item, i){
if(item.name==value){
count=count+1;
}
angular.forEach(item.examples, function(exp, j){
if(exp.name==value){
count=count+1;
}
})
});
console.log("count"+count);
return count;
}
var result=$scope.findRowCount("second");
console.log(result);
}
http://jsfiddle.net/p3g9vyud/

Try this way
var myApp = angular.module('myApp', []).controller('MyCtrl', MyCtrl);
function MyCtrl($scope) {
$scope.items = [{
name: 'first',
examples: [{
name: 'first 1'
}, {
name: 'first 2'
}]
}, {
name: 'second',
examples: [{
name: 'second'
}, {
name: 'second'
}]
}];
//Get sum based on the label
$scope.getTotalByLabel = function(keyword)
{
$scope.totalSecond = 0;
angular.forEach($scope.items, function(value, key) {
if(value.name == keyword)
{
$scope.totalSecond += 1;
}
angular.forEach(value.examples, function(val, k) {
if(val.name == keyword)
{
$scope.totalSecond += 1;
}
});
});
return $scope.totalSecond;
}
}
th,
td {
padding: 7px;
text-align: left;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.1/angular.min.js"></script>
<div ng-app="myApp">
<div ng-controller="MyCtrl">
<table border="1">
<tbody ng:repeat="i in items">
<tr>
<td>{{i.name}}</td>
<td>{{$index}}</td>
</tr>
<tr ng:repeat="e in i.examples">
<td>{{e.name}}</td>
<td>{{$index}}</td>
</tr>
</tbody>
</table>
<b>Total of second</b>: {{getTotalByLabel('second')}}
</div>
</div>

Related

Knockout Table : Highlight a Table Row

I have an Example Fiddle here. In this Table I wish to achieve Highlighting a Particular Row selected. If unselected Row should not be highlighted.
One of many sample I found Fiddle but I am unable to incorporate them inside my Example Fiddle Above.
Below is the HTML Code which shows basic Table.
<table id="devtable">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Status</th>
</tr>
</thead>
<tbody data-bind="foreach: items">
<tr data-bind=" click: $parent.select ">
<td data-bind="text: ID"></td>
<td data-bind="text: Name"></td>
<td data-bind="text: Status"></td>
</tr>
</tbody>
ID :
Name :
Status :
Here is the knockout function to do manipulations
<Script>
var rowModel = function (id, name, status) {
this.ID = ko.observable(id);
this.Name = ko.observable(name);
this.Status = ko.observable(status);
};
var myData = [{
id: "001",
name: "Jhon",
status: "Single"
}, {
id: "002",
name: "Mike",
status: "Married"
}, {
id: "003",
name: "Marrie",
status: "Complicated"
}];
function MyVM(data) {
var self = this;
self.items = ko.observableArray(data.map(function (i) {
return new rowModel(i.id, i.name, i.status);
}));
self.select = function(item) {
self.selected(item);
self.enableEdit(true);
};
self.flashCss = ko.computed(function () {
//just an example
return 'flash';
});
self.selected = ko.observable(self.items()[0]);
self.enableEdit = ko.observable(false);
self.changeTableData = function() {
// How do I change the Data here and it should also reflect on the Page.
// If I do binding depending on condition it gives me error
if(true){
var myData = [{
id: "001",
name: "Jhon",
status: "Single"
}, {
id: "002",
name: "Mike",
status: "Married"
}, {
id: "003",
name: "Marrie",
status: "Complicated"
}];
}
else{
myData = [{
id: "111",
name: "ABC",
status: "Single"
}, {
id: "222",
name: "XYZ",
status: "Married"
}, {
id: "3333",
name: "PQR",
status: "Complicated"
}];
}
}
}
ko.applyBindings(new MyVM(myData));
</script>
CSS code below
.flash { background-color: yellow; }
You can use the css binding to add the .flash class based on the currently selected value:
<tr data-bind="click: $parent.select,
css: { flash: $parent.selected() === $data }">
...
</tr>
If you don't like this logic being defined in the view, you can pass a reference to the selected observable and create a computed property inside your RowModel:
var RowModel = function( /* ... */ selectedRow) {
// ...
this.isSelected = ko.pureComputed(function() {
return selectedRow() === this;
}, this);
}
Here's the quick fix in your fiddle:
http://jsfiddle.net/wa78zoe4/
P.S. if you want toggle-behavior, update select to:
self.select = function(item) {
if (item === self.selected()) {
self.selected(null);
self.enableEdit(false);
} else {
self.selected(item);
self.enableEdit(true);
}
};

AngularJS: ng-repeat without similar key

I have list of objs:
[{
key:test1
name: name1
},
{
key:test1
name: name2
},
{
key:test2
name: name3
}]
And i use ng-repeat to display it:
<tr ng-repeat=item in list>
<td>{{item.key}}</td>
<td>{{item.name}}</td>
</tr>
Is it possible to combine values with similar keys without changing the structure? not to be displayed twice test1 in my case
now:
test1 : name1
test1 : name2
test2 : name3
desired result:
test1 : name1
_____ name2
test2 : name3
You can use groupBy filter:
angular.module('app', ['angular.filter']).controller('ctrl', function($scope){
$scope.list = [{
key:'test1',
name: 'name1'
}, {
key:'test1',
name: 'name2'
},{
key:'test1',
name: 'name3'
},{
key:'test2',
name: 'name4'
}];
})
table, th, td {
border: 1px solid black;
border-collapse: collapse;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-filter/0.5.16/angular-filter.js"></script>
<table ng-app='app' ng-controller='ctrl'>
<tbody>
<tr ng-repeat-start="(key, value) in list | groupBy: 'key'">
<td>{{key}}</td>
<td>{{value[0].name}}</td>
</tr>
<tr ng-repeat-end ng-repeat='item in value.splice(1)'>
<td></td>
<td>{{item.name}}</td>
</tr>
</tbody>
</table>
ng-repeat="item in list | unique:'key'"
Here is how you can achieve the common key value in a same place using angular-filter:
angular.module('app',['angular.filter']).controller('mainCtrl', function($scope){$scope.list = [{
key:'test1',
name: 'name1'
},
{
key:'test1',
name: 'name2'
},
{
key:'test2',
name: 'name3'
}]
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.22/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-filter/0.5.16/angular-filter.min.js"></script>
<div ng-app='app' ng-controller='mainCtrl'>
<div ng-repeat="(key, value) in list | groupBy: 'key'">
<span ng-repeat='val in value'>{{val.name}} </span>
</div>
</div>
Before using ng-repeat update the list.
function rd(o, k, v) {
var n = [];
var l = {};
for(var i in o) {
if (l.hasOwnProperty(o[i][k])){
o[i][v] = l[o[i][k]][v]+ " " + o[i][k]
l[o[i][k]] = o[i]
} else{
l[o[i][k]] = o[i];
}
}
for(i in l) {
n.push(l[i]);
}
return n;
}
var list = rd(arr, "key", "name");
You can try this:
var app = angular.module('myApp', ['angular.filter']);
app.controller('myCtrl', function($scope) {
$scope.items = [{
"key":"test1",
"name": "name1"
},
{
"key":"test1",
"name": "name2"
},
{
"key":"test2",
"name": "name3"
}];
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/angular-filter/0.5.16/angular-filter.min.js"></script>
<div ng-app="myApp" ng-controller="myCtrl">
<div ng-repeat="item in items | groupBy: 'key'">
<h3>Key : {{item[0].key}}</h3>
<p>Names : <span ng-repeat='i in item'>{{i.name}} </span></p>
</div>
</div>

Pagination with filters using ng-repeat in angular

I am trying to do a pagination using filters.
There is a list with names and countries.
I am trying to filter them by country and also alphabetical range, and then generate the pagination by numbers. I am really stuck with it. any help will be really appreciate it
The alphabetical filter will retrieve the names that start with the the range of letters. For example if you select the first option [A - M] will return the person that their name start within that range of letters
Here is my code. The html is over there. Thanks
http://jsbin.com/cifowatuzu/edit?html,js,output
angular.module('app',['angular.filter'])
.controller('MainController', function($scope) {
$scope.selectedCountry = '';
$scope.currentPage = 1;
$scope.pageSize = 3;
$scope.pages = [];
//This should store {StartFrom and To from selected Range}
$scope.selectedRange = '';
$scope.AlphabethicalRange = [
{StartFrom: 'A', To: 'M'},
{StartFrom: 'N', To: 'Z'}
];
$scope.Countries = [
{ Name : 'USA'},
{ Name : 'Japan'},
{ Name : 'France'},
{ Name : 'Canada'},
{ Name : 'China'},
];
$scope.People = [
{ Id: 1, Name: 'Will', Country: 'USA'},
{ Id: 2, Name: 'Ed', Country: 'USA' },
{ Id: 3, Name: 'Peter', Country: 'China'},
{ Id: 4, Name: 'John', Country: 'Japan'},
{ Id: 5, Name: 'Alex', Country: 'France'},
{ Id: 6, Name: 'Jim', Country: 'France'},
{ Id: 7, Name: 'Austin', Country: 'Italy'},
{ Id: 8, Name: 'Men', Country: 'France'},
{ Id: 9, Name: 'Zike', Country: 'Canada'},
];
$scope.numberPages = Math.ceil($scope.People.length / $scope.pageSize);
$scope.init = function () {
for (i = 1; i < $scope.numberPages; i++) {
$scope.pages.push(i);
}
};
$scope.init();
});
I create a custom filter to filter the range that you want.
Here's a snippet working:
var app = angular.module('app', ['angular.filter']);
app.controller('mainCtrl', function ($scope) {
$scope.currentPage = 1;
$scope.pageSize = 3;
$scope.pages = [];
$scope.AlphabethicalRange = [
{
"StartFrom":"A",
"To":"M"
},
{
"StartFrom":"N",
"To":"Z"
}
];
$scope.Countries = [
{
"Name":"USA"
},
{
"Name":"Japan"
},
{
"Name":"France"
},
{
"Name":"Canada"
},
{
"Name":"China"
}
];
$scope.People = [
{
"Id":1,
"Name":"Will",
"Country":"USA"
},
{
"Id":2,
"Name":"Ed",
"Country":"USA"
},
{
"Id":3,
"Name":"Peter",
"Country":"China"
},
{
"Id":4,
"Name":"John",
"Country":"Japan"
},
{
"Id":5,
"Name":"Alex",
"Country":"France"
},
{
"Id":6,
"Name":"Jim",
"Country":"France"
},
{
"Id":7,
"Name":"Austin",
"Country":"Italy"
},
{
"Id":8,
"Name":"Men",
"Country":"France"
},
{
"Id":9,
"Name":"Zike",
"Country":"Canada"
}
];
$scope.numberPages = Math.ceil($scope.People.length / $scope.pageSize);
$scope.init = function() {
for (i = 1; i < $scope.numberPages; i++) {
$scope.pages.push(i);
}
};
$scope.init();
});
app.filter('rangeAlphaFilter', function() {
return function(items, search) {
if (!search || search == ' - ') {
return items;
}
return items.filter(function(element) {
return new RegExp('[' + search.replace(/ /g, '') + ']', 'i').test(element.Name[0]);
});
}
});
<!DOCTYPE html>
<html ng-app="app">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.7/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-filter/0.5.8/angular-filter.min.js"></script>
</head>
<body ng-controller="mainCtrl">
<div>
<span>Country Filter</span>
<select name="countriesSelect" ng-options="c as c.Name for c in Countries" ng-model="selectedCountry">
<option value="">-- Select a country --</option>
</select>
<br>
<span>Alphabetical Filter</span>
<select name="AlphabeticalSelect" ng-options="a as a.StartFrom +' - '+ a.To for a in AlphabethicalRange" ng-model="selectedRange">
<option value="">-- Select a range --</option>
</select>
<ul>
<li ng-repeat="person in People | filter: { Country: selectedCountry.Name } | rangeAlphaFilter: selectedRange.StartFrom +' - '+ selectedRange.To" ng-bind="person.Name"></li>
</ul>
<span>Pagination Numbers</span>
{{page}}
</div>
</body>
</html>
PS: To control the pagination, I extremely don't recommend you to do it manually, it gives a lot of work. I recommend you to see my answer in this another question, it's like a "mini" tutorial of how to use the angularUtils-pagination. Check it.
I hope it helps.

Displaying json object details in knockout js

I have the following fiddle where I am trying to display the data in key:value pairs,
i.e., key as header and followed by the information as rows .
I have the data in this format:
self.data = ko.observableArray([{
1:
{
name: 'Name 1',
lastLogin: '8/5/2012'
}
}
, {
2:
{
name: 'Name 2',
lastLogin: '2/8/2013'
}
}
]);
I have fiddle as :
https://jsfiddle.net/1988/z7nnf0fh/1/
I am expecting as:
1
name Name 1 lastLogin 8/5/2012
2
name Name 2 lastLogin 2/8/2013
I'd personally move all logic to your viewmodel. Then you could either use ko.toJSON to stringify the contents of each object or if you really want to have the output like above, you could do:
function DataModel() {
var self = this;
self.data = ko.observableArray([{
1: {
name: 'Name 1',
lastLogin: '8/5/2012'
}
}, {
2: {
name: 'Name 2',
lastLogin: '2/8/2013'
}
}
]);
self.formattedValues = ko.observableArray([]);
self.formatData = function() {
var tempRow = [];
ko.utils.arrayForEach(self.data(), function(item) {
for (var i in item) {
for (var j in item[i]) {
tempRow.push({
key: j,
value: item[i][j]
});
}
self.formattedValues.push({
key: i,
rows: tempRow
});
tempRow = [];
}
})
};
self.formatData();
}
var dataModel = new DataModel();
ko.applyBindings(dataModel);
.name {
color: #bbb;
}
.value {
fot-weight: bold
}
th {
width: 25px;
}
p {
margin-right: 10px;
display: inline-block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<div data-bind="template: { name: 'template', data: formattedValues }"></div>
<script type="text/html" id="template">
<table>
<tbody data-bind="foreach: $data">
<tr>
<td data-bind="text: key"></td>
</tr>
<tr>
<td data-bind="foreach: rows">
<p>
<span class="name" data-bind="text: key + ': '"></span>
<span class="value" data-bind="text: value"></span>
</p>
</td>
</tr>
</tbody>
</table>
</script>
Hope that helps in some way

Angular ng-repeat for duplicates

I use Angular, this is my view:
<div class="col-sm-6" ng-repeat="contact in service.contacts">
<label class="control-label mb10">{{contact.textDescription | decodeURIComponent}}</label>
<select multiple="multiple" selectize>
<option>{{contact.value}}</option>
</select>
</div>
I have one problem and I can't figure out a way to solve it. For contact.textDescription, I need to put only unique values, so if that contact.textDescription is duplicate, don't create field twice, but add that contact.value to options of that same contact.textDescription which already exists.. So, something like this:
if(contact.textDescription is duplicate) {
contact.value.addTo(contact.textDescription which already exists)
}
Maybe some filter to apply or?
By the understanding of what you mentioned I think the filter mention in the following answer will help you to do what you want
Use this link to go to the question
You can use ng-options to group and display unique value.
You can use uniqFilter of angular-filter module.
Usage: collection | unique: 'property' or nested.property
Aliases: uniq
Example:
JS:
function MainController ($scope) {
$scope.orders = [
{ id:1, customer: { name: 'John', id: 10 } },
{ id:2, customer: { name: 'William', id: 20 } },
{ id:3, customer: { name: 'John', id: 10 } },
{ id:4, customer: { name: 'William', id: 20 } },
{ id:5, customer: { name: 'Clive', id: 30 } }
];
}
HTML:
<th>Customer list:</th>
<tr ng-repeat="order in orders | unique: 'customer.id'" >
<td> {{ order.customer.name }} , {{ order.customer.id }} </td>
</tr>
RESULT:
Customer list:
- John 10
- William 20
- Clive 30
Grouping your contacts by textDescription should resolve your problem. Try something like this:
html:
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Example - GroupBy Filter</title>
<script src="angular-1.4.7.min.js"></script>
<script src="script.js"></script>
</head>
<body ng-app="app">
<div ng-controller="MainController">
<div ng-repeat="(key, contacts) in (service.contacts | groupBy: 'textDescription')">
<label class="control-label">{{key}}</label>
<select multiple="multiple" selectize>
<option ng-repeat="contact in contacts">{{contact.value}}</option>
</select>
</div>
</div>
</body>
script.js:
var app = angular.module("app", []);
app.controller("MainController", function ($scope) {
$scope.service = {};
$scope.service.contacts = [{
"textDescription": "td1",
"value": "1"
}, {
"textDescription": "td2",
"value": "2"
}, {
"textDescription": "td3",
"value": "3"
}, {
"textDescription": "td1",
"value": "4"
}, {
"textDescription": "td3",
"value": "5"
}, {
"textDescription": "td1",
"value": "6"
}];
});
app.filter('groupBy', function () {
var results={};
return function (data, key) {
if (!(data && key)) return;
var result;
if(!this.$id){
result={};
}else{
var scopeId = this.$id;
if(!results[scopeId]){
results[scopeId]={};
this.$on("$destroy", function() {
delete results[scopeId];
});
}
result = results[scopeId];
}
for(var groupKey in result)
result[groupKey].splice(0,result[groupKey].length);
for (var i=0; i<data.length; i++) {
if (!result[data[i][key]])
result[data[i][key]]=[];
result[data[i][key]].push(data[i]);
}
var keys = Object.keys(result);
for(var k=0; k<keys.length; k++){
if(result[keys[k]].length===0)
delete result[keys[k]];
}
return result;
};
});
Now you have all the contacts grouped by textDescription, so the field for it (<label>) is created only once and the values are added to <option> tags

Categories