How to calculate sum of rows in ng-repeat? - javascript

i have a slight different requirement,request you to please read through before marking as duplicate.
Given the example:
<table ng:init="stuff={items:[{description:'gadget', cost:99,date:'jan3'},{description:'thing', cost:101,date:'july6'},{description:'thing', cost:101,date:'jan3'} ]}">
<tr>
<th>Description</th>
<th>Cost</th>
</tr>
<tr ng:repeat="item in stuff.items|filter"> /*only filtered item grouped by date*/
<td>{{item.description}}</td>
<td ng-bind='item.cost'>{{item.cost}}</td>
</tr>
<tr>
<td></td>
<td>{{total}}</td> /*cost of items grouped by date jan3*/
</tr>
</table>
How do i calculate total cost of group by items?Is there any data-attribute in angular where I can add the cost for a grouped item,then again re-initialize it for the next grouped items?

Angular 1.3 added the ability to create an alias to your ng-repeat, which is very useful when used in conjunction with a filter.
variable in expression as alias_expression – You can also provide an optional alias expression which will then store the intermediate results of the repeater after the filters have been applied. Typically this is used to render a special message when a filter is active on the repeater, but the filtered result set is empty.
For example: item in items | filter:x as results will store the fragment of the repeated items as results, but only after the items have been processed through the filter.
So, you can use this as alias_expression to perform a calculation on the filtered subset of your list. i.e.:
<tr ng-repeat="item in stuff.items|filter as filteredStuff">
{{filteredStuff.length}}
{{calculateTotal(filteredStuff)}}
</tr>
in controller:
$scope.calculateTotal = function(filteredArray){
var total = 0;
angular.forEach(filteredArray, function(item){
total += item.cost;
});
return total;
};

You could create your own custom filter, that will accept the array will return you the total cost of your all items.
Markup
<tr ng:repeat="item in filteredData = (stuff.items|filter)">
<td>{{item.description}}</td>
<td ng-bind='item.cost'>{{item.cost}}</td>
</tr>
<tr>
<td></td>
<td>{{filteredData| total}}</td> /*cost of items grouped by date jan3*/
</tr>
Code
app.filter('total', function(){
return function(array){
var total = 0;
angular.forEach(array, function(value, index){
if(!isNaN(value.cost))
total = total + parseFloat(value.cost);
})
return total;
}
})

Related

jQuery how to filter for elements having a certain data attribute [duplicate]

This question already has answers here:
jQuery attribute selector variable
(3 answers)
Closed 3 years ago.
I have a table with the following format:
<table data-type="costTable">
<thead>...</thead>
<tbody>
<tr data-grouping='category' data-category='A'>...</tr>
<tr data-grouping='subCategory' data-category='A'>...</tr>
<tr data-grouping='subCategory' data-category='A'>...</tr>
...
<tr data-grouping='category' data-category='B'>...</tr>
<tr data-grouping='subCategory' data-category='B'>...</tr>
<tr data-grouping='subCategory' data-category='B'>...</tr>
...
</tbody>
</table>
Initially, I've added class='hidden' using jQuery to all subCategory rows. When the user clicks on a category, say category A, I'd like to remove the class hidden from its sub-categories, e.g. a1, a2, and a3. The following code does not work:
$(function () {
var categoryRows = $("table[data-type='costTable'] tr[data-grouping='category']");
var subCategoryRows = $("table[data-type='costTable'] tr[data-grouping='subCategory']");
var itemRows = $("table[data-type='costTable'] tr[data-grouping='item']");
var subItem = $("table[data-type='costTable'] tr[data-grouping='subItem']");
collapseInitial(subCategoryRows);
collapseInitial(itemRows);
if (subItem){
collapseInitial(subItem);
}
// Toggle subCategory rows:
categoryRows.click( function(){
// Clicking on the category rows should display their subcategory
var categoryName = $(this).data('category');
var targettedElements = subCategoryRows.filter("[data-category=categoryName]");
toggleElement(targettedElements);
});
});
I've done some debugging in the browser, and the problem is in the following line:
var targettedElements = subCategoryRows.filter("[data-category=categoryName]");
When clicking on a category row A, it should find the subcategory rows with the data-category A, but it find nothing. Note that when I inspect the targettedElements, it shows
r.fn.init [prevObject: r.fn.init(30)]
I have also tried .has function instead of .filter, but that does not make any change in the return value. Note that when I hard-code a category name where I have categoryName, it does return the right elements. So, even though categoryName is shown as a string in the console, it is not replaced as such. Any idea how to fix this?
This is just a string, you are not using it as the variable.
.filter("[data-category=categoryName]");
basic string concatenation
.filter('[data-category="' + categoryName + '"]');
If you wish to use the jQuery's filter function, go to epascarello's answers.
Otherwise, here is an example using the vanilla filter function.
You could get your jquery rows as an array and use the filter function to filter out unwanted rows.
Please take note that i've copied only some rows of your code for simplicity, but the behaviour is the same.
// we use to array in order to be able to use the filter function afterward.
var subCategoryRows = $("table[data-type='costTable'] tr[data-grouping='subCategory']").toArray();
var categoryName = 'B';
var targettedElements = subCategoryRows.filter((row) => {
// compared the current row data to the "clicked" category.
return $(row).data('category') === categoryName;
});
// our final targettedElements array is now 2 in length;
console.log(targettedElements.length);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table data-type="costTable">
<thead>...</thead>
<tbody>
<tr data-grouping='category' data-category='A'>...</tr>
<tr data-grouping='subCategory' data-category='A'>...</tr>
<tr data-grouping='subCategory' data-category='A'>...</tr>
<tr data-grouping='category' data-category='B'>...</tr>
<tr data-grouping='subCategory' data-category='B'>...</tr>
<tr data-grouping='subCategory' data-category='B'>...</tr>
</tbody>
</table>

NodeJS: How can I scrape two different tables, that are visually part of the same table, into one JSON Object?

Here's an example of the table of data I'm scraping:
The elements in red are in the <th> tags while the elements in green are in a <td> tag, the <tr> tag can be displayed according to how they're grouped (i.e. '1' is in it's own <tr>; HTML snippet:
EDIT: I forgot to add the surrounding div
<div class="table-cont">
<table class="tg-1">
<thead>
<tr>
<th class="tg-phtq">ID</td>
</tr>
</thead>
<tbody>
<tr>
<td class="tg-0pky">1</td>
<td class="tg-0pky">2</td>
<td class="tg-0pky">3</td>
</tr>
</tbody>
</table>
<table class="tg-2">
<thead>
<tr>
<th class="tg-phtq">Sample1</td>
<th class="tg-phtq">Sample2</td>
<...the rest of the table code matches the pattern...>
</tr>
</thead>
<tbody>
<tr>
<td class="tg-0pky">Swimm</td>
<td class="tg-dvpl">1:30</td>
<...>
</tr>
</tbody>
<...the rest of the table code...>
</table>
</div>
As you can see, in the HTML they're actually two different tables while they're displayed in the above example as only one. I want to generate a JSON object where the keys and values include the data from the two tables as if they were one, and output a single JSON Object.
How I'm scraping it right now is a bit of modified javascript code I found on a tutorial:
EDIT: In the below, I've been trying to find a way to select all relevant <th> tags from both tables and insert them into the same array as the rest of the <th> tag array and do the same for <tr> in the table body; I'm fairly sure for the th I can just insert the element separately before the rest but only because there's a single one - I've been having problems figuring out how to do that for both arrays and make sure all the items in the two arrays map correctly to each other
EDIT 2: Possible solution? I tried using XPath Selectors and I can use them in devTools to select everything I want, but page.evaluate doesn't accept them and page.$x('XPath') returns JSHandle#node since I'm trying to make an array, but I don't know where to go from there
let scrapeMemberTable = async (page) => {
await page.evaluate(() => {
let ths = Array.from(document.querySelectorAll('div.table-cont > table.tg-2 > thead > tr > th'));
let trs = Array.from(document.querySelectorAll('div.table-cont > table.tg-2 > tbody > tr'));
// the above two lines of code are the main problem area- I haven't been
//able to select all the head/body elements I want in just those two lines of code
// just removig the table id "tg-2" seems to deselect the whole thing
const headers = ths.map(th => th.textContent);
let results = [];
trs.forEach(tr => {
let r = {};
let tds = Array.from(tr.querySelectorAll('td')).map(td => td.textContent);
headers.forEach((k,i) => r[k] = tds[i]);
results.push(r);
});
return results; //results is OBJ in JSON format
}
}
...
results = results.concat( //merge into one array OBJ
await scrapeMemberTable(page)
);
...
Intended Result:
[
{
"ID": "1", <-- this is the goal
"Sample1": "Swimm",
"Sample2": "1:30",
"Sample3": "2:05",
"Sample4": "1:15",
"Sample5": "1:41"
}
]
Actual Result:
[
{
"Sample1": "Swimm",
"Sample2": "1:30",
"Sample3": "2:05",
"Sample4": "1:15",
"Sample5": "1:41"
}
]

How to put numbers being pulled from API list into ascending order?

$(function() {
var companyId = 1740963;
JA.get('api/' + companyId + '/Transaction', function(data) {
if (!data)
return;
var items = data.Items;
if (!items)
return;
for (var i = 0; i < items.length; i++) {
var expense = items[i];
JA.renderTemplateFromPath('expenseListRow', items[i], function(template) {
var $template = $(template);
$('#expenseListBody').append($template);
$template.find('.expenseDate p').html(function(index, value) {
return moment(value, "YYYY-MM-DDTHH:mm:ss").format("DD/MM/YYYY");
});
})
}
});
})
<table id="expenseList">
<tr id="expenseListHead">
<th id="numberID">Number</th>
<th>Date</th>
<th>Name</th>
<th>Description</th>
<th>Reference</th>
<th class="alignRight">Amount</th>
<th class="alignRight"></th>
</tr>
<tbody id="expenseListBody">
<tr class="expenseListRow">
<td></td>
<td class="expenseDate"></td>
<td></td>
<td></td>
<td></td>
<td class="alignRight"></td>
<td class="alignRight"></td>
</tr>
</tbody>
</table>
As you can see in the image, there are multiple numbers in random order being pulled from an API, the list carry's on for a while.
What I am wondering is if anyone could help me work out how to sort these numbers in ascending order (starting from 1) and so on, instead of it adding them randomly, like I said this data is being pulled from an API that has been written so I'm struggling to work out how to do this. If anymore info is needed to help answer let me know. Thanks in advance.
If your list contains only numbers you can use the sort function available with Array. just call
var sortedList = yourList.sort();
If its an array of objects and the numbers are inside as a field then you can override the sort function as mentioned here

AngularJS ng-repeat column aware table

I have the following model: Item : {data: String, schedule: DateTime, category: String}
I need to display a report of this data in the following way:
<table>
<tr>
<th>Time Range</th>
<th>Category 1</th>
<th>Category 2</th>
<th>No Category</th>
</tr>
<tr>
<td>6:00 - 2:30</td>
<td>3</td>
<td>5</td>
<td>7</td>
</tr>
</table>
So I will need to compile and filter the list of Items into the time ranges and display the totals based on categories. How can I accomplish this with angular and tell which column I am in (and therefore choose the right value to display) as the categories are dynamic.
EDIT: The table will display a summary of all Items. So you'll have take all items and compile it into the form Time Range, # In Category 1, # In Category 2, # In No Category. The only thing that will be set by me is the time ranges.
I plan to store this data in a hash map where the keys are the category names, but I need to know which column I am in as the categories are dynamic. The categories come from the database (a user putting these items in).
Basically you need to do two things. Group list by category and display grouped list. See JsFiddle: https://jsfiddle.net/vittore/mmvxbcjx/4/
In order to group list you can use array.reduce. Convenient data structure for that would be hash of hashes, ie
var hash = {
'6:30': {
category1: 5
}
}
(Say you are grouping datetime based on time with an hour step.)
In order to get structure like that with reduce you will do:
var myList = [{},....];
$scope.grouped = list.reduce(function(a, d) {
var hours = d.schedule.getHours();
if (!(hours in a)) a[hours] = {}
var timeSlot = a[hours]
timeSlot[d.category || 'no category' ] =
1 + ( timeSlot[d.category || 'no category'] | 0)
return a;
}, {})
After you've got desired structure you need to do nested ng-repeat with angular:
<tr>
<th>time slot</th>
<th ng-repeat='c in categories'>{{ c }} </th>
<th>no category</th>
</tr>
<tr ng-repeat='(k,v) in grouped'>
<th>{{ k }} : 00</th>
<td ng-repeat='c in categories'>{{ v[c] }} </td>
<td>{{ v['no category'] }}</td>
</tr>

Read the first column values of a HTML table with jquery

I got a table:
<table id="ItemsTable" >​
<tbody>
<tr>
<th>
Number
</th>
<th>
Number2
</th>
</tr>
<tr>
<td>32174711</td> <td>32174714</td>
</tr>
<tr>
<td>32174712</td> <td>32174713</td>
</tr>
</tbody>
</table>
I need the values 32174711 and 32174712 and every other value of the column number into an array or list, i'm using jquery 1.8.2.
var arr = [];
$("#ItemsTable tr").each(function(){
arr.push($(this).find("td:first").text()); //put elements into array
});
See this link for demo:
http://jsfiddle.net/CbCNQ/
You can use map method:
var arr = $('#ItemsTable tr').find('td:first').map(function(){
return $(this).text()
}).get()
http://jsfiddle.net/QsaU2/
From jQuery map() documentation:
Description: Pass each element in the current matched set through a function, producing a new jQuery object containing the return values.
.
As the return value is a jQuery-wrapped array, it's very common to get() the returned object to work with a basic array.
// iterate over each row
$("#ItemsTable tbody tr").each(function(i) {
// find the first td in the row
var value = $(this).find("td:first").text();
// display the value in console
console.log(value);
});
http://jsfiddle.net/8aKuc/
well from what you have, you can use first-child
var td_content = $('#ItemsTable tr td:first-child').text()
// loop the value into an array or list
http://jsfiddle.net/Shmiddty/zAChf/
var items = $.map($("#ItemsTable td:first-child"), function(ele){
return $(ele).text();
});
console.log(items);​

Categories