Transpose dynamic table from rows to columns and vice versa - javascript

config.previewData = [
{
Cartridges:27989,
Total Accounts:294,
Metrices:"MVC",
Toner Cartridges:5928,
INK Cartridges:22061
},
{
Cartridges:56511,
Total Accounts:376,
Metrices:"SMB",
Toner Cartridges:15253,
INK Cartridges:41258
},
{
Cartridges:84,500,
Total Accounts:670,
Metrices:"Grand Total",
Toner Cartridges:21,181,
INK Cartridges:63,319
},
]
and my html code like this
<table class="table table-striped">
<thead>
<tr role="row">
<th data-ng-repeat="(key, val) in config.previewData[0]">
{{ key}}
</th>
</tr>
</thead>
<tbody>
<tr data-ng-repeat="row in config.previewData">
<td data-ng-repeat="column in row">
{{column}}
</td>
</tr>
</tbody>
</table>
this code will print perfect like below image
now i want to transpose this table into rows to columns and columns to rows.
Is this possible with dynamic table because my object is dynamic not fixed.
Help me if anyone knows how to do this.
After transpose table looks like this

Using the same assumptions your example codes does (i.e. config.previewData always contains at least one object, and all objects have the same properties...)
<table class="table table-striped">
<tbody>
<tr data-ng-repeat="(key, val) in config.previewData[0]">
<th>
{{ key }}
</th>
<td data-ng-repeat="row in config.previewData">
{{ row[key] }}
</td>
</tr>
</tbody>
</table>

Using reduce, you can have something like this to transpose your data, which can then be used to iterate over using ng-repeat very easily!
Example snippet (in Pure JS for simplification):
var previewData = [{
"Cartridges": 27989,
"Total Accounts": 294,
"Metrices": "MVC",
"Toner Cartridges": 5928,
"INK Cartridges": 22061
},
{
"Cartridges": 56511,
"Total Accounts": 376,
"Metrices": "SMB",
"Toner Cartridges": 15253,
"INK Cartridges": 41258
},
{
"Cartridges": 84500,
"Total Accounts": 670,
"Metrices": "Grand Total",
"Toner Cartridges": 21181,
"INK Cartridges": 63319
}
]
var transpose = previewData.reduce(function(arr, obj) {
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
arr[key] = arr[key] || []
arr[key].push(obj[key])
}
}
return arr
}, {})
console.log(transpose)

this is the only (dirty) way i could think out
<tr>
<td data-ng-repeat="row in previewData">{{row['Metrices']}}</td>
</tr>
<tr>
<td data-ng-repeat="row in previewData">{{row['Total Accounts']}}</td>
</tr>
<tr>
<td data-ng-repeat="row in previewData">{{row['Toner Cartridges']}}</td>
</tr>
...... and so on
other options: Transposing JSON

If you have a 2-D array which can be logged into console by the function
tab = [[2,3,4],[-4,6,0],[1,0,9]]
console.table(tab)
You can log the transpose of it by using the following function:
function transpose_table(tab) {
let columns = tab.length;
let rows = tab[0].length;
let trans = [];
for (i=0; i<rows; i++) {
trans.push([]);
}
for (i=0; i<columns; i++) {
for (j=0; j<rows; j++) {
trans[j][i] = tab[i][j];
}
}
return trans;
}
Now run:
console.table(transpose_table(tab))

Related

I'd like to create a JSON object out of an HTML table. I'd like the end product to be used to tally input from multiple users

Active learner here, trying to figure out how to create a JSON object out of HTML table. I only want the value of one specific TD and want to give each value an incrementing number as a key. I'd like an output like below. My table has a TD for the city names, but it does not have one with a incrementing numerical value so I'd need to add that another way.
{
"mycities" : [
{
"Seattle" : "1",
"Chicago" : "2",
"New York" : "3"
"Pitt" : "4",
"LA" : "5",
"Fresno" : "6"
},
]
}
Here is what my table looks like:
<table>
<thead>
<tr>
<th>city name</th>
<th>other city info</th>
</tr>
</thead>
<tbody>
<tr>
<td>Seattle</td>
<td>Lots of rain</td>
</tr>
etc,etc,etc
</tbody>
</table>
I've tried using a replacer function but haven't got it figured out after much googling. Any help is appreciated!
$(document).ready(function(){
$("body").on("click",".submitButtonPri",function(){
count= 1;
function replacer(key, value) {
if (typeof value === 'string') {
return count;
}
return value;
}
var myRows = [];
var $headers = $(".rightDash > table thead th");
var $rows = $(".rightDash > table tbody tr").each(function(index) {
$cells = $(this).find("td.titlePri");
myRows[index] = {};
$cells.each(function(cellIndex) {
myRows[index][$($cells[cellIndex]).text()] = $(this).text();
});
count++;
});
var myObj = {};
myObj.myrows = myRows;
console.log(JSON.stringify(myObj,replacer));
});
});
Use reduce to iterate over the trs in the body, using the text content of the first td in the tr as the city name. The third argument to the function provided to reduce represents the iteration index:
const cityData = [...document.querySelectorAll('tbody > tr')]
.reduce((a, tr, i) => {
a[tr.children[0].textContent] = i + 1;
return a;
}, {});
console.log(
{ mycities: [
cityData
]}
);
<table>
<thead>
<tr>
<th>city name</th>
<th>other city info</th>
</tr>
</thead>
<tbody>
<tr>
<td>Seattle</td>
<td>Lots of rain</td>
</tr>
<tr>
<td>Chicago</td>
<td>Lots of rain</td>
</tr>
<tr>
<td>New York</td>
<td>Lots of rain</td>
</tr>
</tbody>
</table>
You can start with this simple script:
$('td').each(function(index, obj) {console.log(index, $(this).html())});
It returns all what you need and you just need assemble JSON by any way

How to set index values to child rows in angular js

I have a requirement where I need to add index values for child rows. I have Group rows under which there will be child rows. I am ng-repeat and I am using $index for child's as shown:
HTML code:
<table ng-repeat="node in data">
<tr> <td> {{node.groupName}} </td> </tr>
<tbody ng-model="node.nodes">
<tr ng-repeat="node in node.nodes"> <td> {{$index}} </td> </tr>
</table>
But it is displaying as shown:
But I want it to display as shown:
I am new to Angular JS and not getting how to display it like this. How am I supposed to do that. Please help.
As far as I understood your question, you'd like to have something like that:
<table ng-repeat="group in data">
<thead>
<th> {{group.name}} </th>
</thead>
<tbody ng-repeat="item in group.items">
<tr>
<td>{{getIndex($parent.$index - 1, $index)}} | {{item}} </td>
</tr>
</tbody>
</table>
$scope.data = [
{name: 'Group1', items: ['a','b']},
{name: 'Group2', items: [1,2,3]},
{name: 'Group3', items: ['x', 'xx', 'xxx', 'xxxx']}
];
$scope.getIndex = function(previousGroupIndex, currentItemIndex){
if(previousGroupIndex >= 0){
var previousGroupLength = getPreviousItemsLength(previousGroupIndex);
return previousGroupLength + currentItemIndex;
}
return currentItemIndex;
};
function getPreviousItemsLength(currentIndex){
var length = 0;
for (var i = 0; i <= currentIndex; i++){
length += $scope.data[i].items.length;
}
return length;
// or even better to use Array.prototype.reduce() for that purpose
// it would be shorter and beautiful
// return $scope.data.reduce(function(previousValue, currentGroup, index){
// return index <= previousGroupIndex ? previousValue + currentGroup.items.length : previousValue;
// }, 0);
}
You need to play with $parent.$index property and use some math :) in order to achieve that.
It would look like the following:
Check out this JSFiddle to see live example.

generating table with text box

I've a bit of experience coding in php but am fairly new to js. What I'm trying to do in js is create a simple order form, each line is to have a text box indicating the quantity to be ordered, product name and product price, with the latter to be populated from product array prod. My fairly rudimentary first attempt appears below, which needless to say doesn't work.
<body onload="populate()">
<table id="demo">
<thead>
<tr>
<th>Quantity</th>
<th>Product</th>
<th>Price</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<script>
function populate(){
var prod; //array of objects with name and price attributes
var table = document.getElementById("theTable");
for (var i=0; i<prod.length; i++)
{
var newTr = table.insertRow(-1);
var numOrdered=document.createElement('input');
numOrdered.type='text';
numOrdered.id= "product "+i; //assigning id of "product i" to each product i
newTr.insertCell(0).appendChild(num);
newTr.insertCell(-1).appendChild(document.createTextNode(prod["name"]));
newTr.insertCell(-1).appendChild(document.createTextNode(prod["price"]));
}
}
</script>
</body>
Any and all help appreciated.
have a look at the snippet at the bottom.
what's changed is:
in this line you targeted the wrong id, should have been 'demo'
var table = document.getElementById("theTable");
you also needed to reference the correct value in your array inside the loop:
document.createTextNode(prod["name"]);
to:
document.createTextNode(prod[i]["name"]);
and lastly this line:
newTr.insertCell(0).appendChild(num);
to:
newTr.insertCell(0).appendChild(numOrdered);
hope this helps.
function populate() {
var prod = [{
name: 'box',
price: 20
}, {
name: 'plane',
price: 40
}]; //array of objects with name and price attributes
var table = document.getElementById("theTable");
for (var i = 0; i < prod.length; i++) {
var newTr = table.insertRow(-1);
var numOrdered = document.createElement('input');
numOrdered.type = 'text';
numOrdered.id = "product " + i; //assigning id of "product i" to each product i
newTr.insertCell(0).appendChild(numOrdered);
newTr.insertCell(-1).appendChild(document.createTextNode(prod[i]["name"]));
newTr.insertCell(-1).appendChild(document.createTextNode(prod[i]["price"]));
}
}
populate();
<table id="theTable">
<thead>
<tr>
<th>Quantity</th>
<th>Product</th>
<th>Price</th>
</tr>
</thead>
<tbody></tbody>
</table>
Try This code:
I am using jQuery, to create, append and iterating the array(each Loop).It is a better and effective way. Please use jQuery for DOM manipulation.
HTML:
<table id="demo">
<thead>
<tr>
<th>Quantity</th>
<th>Product</th>
<th>Price</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
CSS:
table,td{display:block}
th{width:100px}
td{margin:10px}
JS:
var prod=[{product:'samsung',price:100},{product:'apple',price:200},{product:'micromax',price:300}];
(function($){
$.each(prod,function(index,value){
$('#demo th:eq(0)').append("<td><input type='text'></td>");
$('#demo th:eq(1)').append("<td>"+value.product.toUpperCase()+"</td>");
$('#demo th:eq(2)').append("<td>"+value.price+"</td>")
});
})(jQuery);
Here is the JSFiddle Link:
https://jsfiddle.net/Dee0565/8vww1pwf/

Meteor returning NaN in the browser

I'm building a simple ordering app in Meteor and have come a cropper trying to get the order total even though I can get it to log in the console as a bona fide number - it is being rendered as NaN. Any help would be greatly appreciated.
Note the totals of individual products are appearing fine.
I have the following files:
meteorder/client/templates/orders/order_build.js:
Template.order.helpers({
'orderitems': function() {
var orderCart = [];
var orderItems = OrderItems.find({});
var total = 0;
orderItems.forEach(function(orderItem) {
var item = _.extend(orderItem, {});
var product = Products.findOne({
_id: orderItem.product
});
orderItem.productname = product.description;
orderItem.price = (Number(product.price) * orderItem.qty);
total += orderItem.price;
orderCart.push(orderItem);
});
orderCart.subtotal = total;
orderCart.tax = orderCart.subtotal * .23;
orderCart.total = orderCart.subtotal + orderCart.tax;
return orderCart;
}
})
meteorder/client/templates/orders/order_build.html:
<template name="order">
<div class="page-header">
<h1>
<small>My Order</small>
</h1>
</div>
<table class="span4 table table-striped table-bordered table-hover">
<thead>
<th>Qty</th>
<th>Product</th>
<th>Price</th>
<th></th>
</thead>
<tbody>
{{#each orderitems}}
<tr>
<td>{{qty}}</td>
<td>{{productname}}</td>
<td>{{currency price}}</td>
<td><span class="label-important label removeci">X</span></td>
</tr>
{{else}}
<tr>
<td colspan="3">No Products in Order</td>
</tr>
{{/each}}
<tr>
<td class="totalcol" colspan="2">SubTotal:</td>
<td colspan="2">{{currency orderCart.subtotal}}</td>
</tr>
<tr>
<td class="totalcol" colspan="2">Tax 6%:</td>
<td colspan="2">{{currency orderCart.tax}}</td>
</tr>
<tr>
<td class="totalcol" colspan="2">Total:</td>
<td colspan="2">{{currency orderCart.total}}</td>
</tr>
</tbody>
</table>
</template>
meteorder/client/lib/main.js:
Template.registerHelper('currency', function(num){
return '€' + Number(num).toFixed(2);
});
meteorder/server/server.js:
Meteor.methods({
addToOrder:function(qty,product,session){
check(qty, Number);
check(product, String);
check(session, String);
if(qty > 0){
OrderItems.insert({qty:qty,product:product,sessid:session});
console.log('reaching this fn', typeof qty, typeof product, typeof session);
} else{
console.log('Quantity is Zero');
}
},
removeOrderItem:function(id){
check(id, String);
OrderItems.remove({_id:id});
console.log('successfully deleted');
}
});
Here is a link to the GitHub repo
And the latest version of the deployed App
Thanks in advance!
Edit:
Just adding this in for Matthias:
Template.productItem.events({
'click .addOrder':function(evt,tmpl){
var qty1 = tmpl.find('.prodqty').value;
var qty = parseInt(qty1);
var product = this._id;
var sessid = Meteor.default_connection._lastSessionId; //stops others adding to your cart etc
Meteor.call('addToOrder',qty, product, sessid);
console.log('this is the quantity:', typeof qty, product, sessid);//stops others ad
}
});
to see if it gives a better picture of why the cart is not populating. Thanks
You're trying to use orderCart as both an array of objects and an object. You push a bunch of orderItem objects on to the array but at the end you attempt to set orderCart.subtotal etc...
Change your helper to have a separate summary object:
var summary = {};
summary.subtotal = total;
summary.tax = summary.subtotal * .23;
summary.total = summary.subtotal + summary.tax;
return {items: orderCart, summary: summary}
Then in your html do:
{{#each orderitems.items}}
...
{{/each}}
Finally in your summary line use {{currency orderitems.summary.tax}} etc...
Your values are rendered as NaN because orderCart is undefined.
You could define a separate helper to fix your code:
Template.order.helpers({
orderItems: function () {
return OrderItems.find().map((orderItem) => {
let product = Products.findOne({
_id: orderItem.product
});
if (product) {
orderItem.productname = product.description;
orderItem.price = calcPrice(product, orderItem);
return orderItem;
}
});
},
orderCart: function () {
let orderCart = {subtotal: 0};
OrderItems.find().forEach((orderItem) => {
let product = Products.findOne({
_id: orderItem.product
});
if (product) orderCart.subtotal += calcPrice(product, orderItem);
});
orderCart.tax = orderCart.subtotal * .23;
orderCart.total = orderCart.subtotal + orderCart.tax;
return orderCart;
}
});
function calcPrice(product, orderItem) {
return (Number(product.price) * orderItem.qty);
}
<template name="order">
<div class="page-header">
<h1>
<small>My Order</small>
</h1>
</div>
<table class="span4 table table-striped table-bordered table-hover">
<thead>
<th>Qty</th>
<th>Product</th>
<th>Price</th>
<th></th>
</thead>
<tbody>
{{#each orderItems}}
<tr>
<td>{{qty}}</td>
<td>{{productname}}</td>
<td>{{currency price}}</td>
<td><span class="label-important label removeci">X</span></td>
</tr>
{{else}}
<tr>
<td colspan="3">No Products in Order</td>
</tr>
{{/each}}
{{#with orderCart}}
<tr>
<td class="totalcol" colspan="2">SubTotal:</td>
<td colspan="2">{{currency orderCart.subtotal}}</td>
</tr>
<tr>
<td class="totalcol" colspan="2">Tax 6%:</td>
<td colspan="2">{{currency orderCart.tax}}</td>
</tr>
<tr>
<td class="totalcol" colspan="2">Total:</td>
<td colspan="2">{{currency orderCart.total}}</td>
</tr>
{{/with}}
</tbody>
</table>
</template>
Please note: I noticed a lot of missing semicolons. I strongly recommend to fix that, as this may cause several issues on deployment due to Meteor's minifying process.

Converting Html Table to JSON

I've created a sample application which converts html table into JSON. The problem is that the JSON is not having duplicate values also i want to remove the last two columns from the JSON.
My JSON which has been generated is given below
[
{
"Person Name":"Smith",
"Score":"disqualified",
"Price":"150",
"Tax":"41"
},
{
"Person Name":"Jackson",
"Score":"94",
"Price":"250",
"Tax":"81"
},
{
"Person Name":"Doe",
"Score":"80",
"Price":"950",
"Tax":"412"
},
{
"Person Name":"Johnson",
"Score":"67",
"Price":"750",
"Tax":"941"
}
]
But my expected JSON is like
[
{
"Person Name":"Jill",
"Person Name":"Smith",
"Score":"disqualified"
},
{
"Person Name":"Eve",
"Person Name":"Smith",
"Score":"94"
},
{
"Person Name":"John",
"Person Name":"Smith",
"Score":"80"
},
{
"Person Name":"Adam",
"Person Name":"Smith",
"Score":"67"
}
]
Can anyone please tell me how to generate the above JSON from the table
My code is as given below.
html code
<table id='example-table'>
<thead>
<tr>
<th>Person Name</th>
<th>Person Name</th>
<th data-override="Score">Points</th>
<th>Price</th>
<th>Tax</th>
</tr>
</thead>
<tbody>
<tr>
<td>Jill</td>
<td>Smith</td>
<td data-override="disqualified">50</td>
<td>150</td>
<td>41</td>
</tr>
<tr>
<td>Eve</td>
<td>Jackson</td>
<td>94</td>
<td>250</td>
<td>81</td>
</tr>
<tr>
<td>John</td>
<td>Doe</td>
<td>80</td>
<td>950</td>
<td>412</td>
</tr>
<tr>
<td>Adam</td>
<td>Johnson</td>
<td>67</td>
<td>750</td>
<td>941</td>
</tr>
</tbody>
</table>
<button id="convert-table" >Convert!</button>
javascript code
$('#convert-table').click( function() {
var table = $('#example-table').tableToJSON();
console.log(table);
alert(JSON.stringify(table));
});
DEMO (JSFiddle)
something like that would work (not really nice, but)
Explanation :
You can use ignoreColumns to avoid taking columns 3 and 4.
You can use headings to change the "headers" (keys in the json file). But this will take also the first line (the one with the TH).
So we have to remove that first line after building the json array.
$('#convert-table').click( function() {
var $table = $('#example-table');
var table = $table.tableToJSON(
{
ignoreColumns:[3, 4],
headings: ['FirstName', 'LastName', 'Score']
});
var newTable = $.map(table, function(e){
return (e.FirstName == "Person Name") ? null : e;
});
console.log(newTable);
alert(JSON.stringify(newTable));
});
see jsfiddle
EDIT
If the number of columns with Person Name is dynamic, you could do something like that (assuming you never want the two last rows)
function convertToTable(el, numberOfColumns, columnNames) {
var columnsToIgnore = [numberOfColumns-2, numberOfColumns-1];
var table = el.tableToJSON(
{
ignoreColumns:columnsToIgnore,
headings: columnNames
});
var result = $.map(table, function(e){
return (e['Person Name0'] == "Person Name") ? null : e;
});
alert(JSON.stringify(result));
}
$('#convert-table').click( function() {
var $table = $('#example-table');
var columns = $table.find('th');
var numberOfColumns = columns.length;
var columnNames = columns.map(function(index) {
var text = $(this).text();
return text == 'Person Name' ? text + index : text;
}).get();
convertToTable($table, numberOfColumns, columnNames);
});
see JsFiddle
You can't have duplicate keys, but you can use an array of names instead. Example:
{
"PersonNames":["John","Smith"],
"Score":"80"
},
to remove last two fields use "ignoreColumns" option
var table = $('#example-table').tableToJSON({
ignoreColumns:[2,3]
});
and make headers unique
<th>Person Name</th>
<th>Person SurName</th>
Try this:
$('#convert-table').click( function() {
var table = $('#example-table').tableToJSON({
ignoreColumns:[3,4]}
);
console.log(table);
alert(JSON.stringify(table));
});
Jsfiddle: http://jsfiddle.net/robertrozas/9VX6Z/
From row a way to format html-data.
$("#del_player").on("click", function() {
var row_to_del = $("#table_player tbody tr[active=true]");
var arr = [];
$.each(row_to_del, function(key, val){
arr.push(val.outerText.split('\t'));
});
console.log(JSON.stringify(arr));
})

Categories