Remove duplicate rows based on 3 columns - javascript

The below code returns a table with values from a web list. Some values are duplicates. I need to remove all duplicates where "User_x0020_Name", "Previous_Total_Most_Likely_Forec", "Submitted_Total_Most_Likely_Fore" are the same and only keep the latest record (maximum of "Created") for a duplicates set.
function loadAuditTrailFinancials() {
var auditTrailURL = _spPageContextInfo.webAbsoluteUrl + "/_api/web/lists/GetByTitle('Audit_Trail_Financial_Data')/items?$select=Author/Title,Previous_Total_Most_Likely_Forec,Submitted_Total_Most_Likely_Fore,Forecast_Approval_Reason,Title,Created,Workflow_Stage_Name,WorkflowStageShort,Source,Event&$filter=Title eq '" + PDP_projUid + "'&$orderby=Created desc&$expand=Author/ID";
console.log("loadAuditTrailFinancials:" + auditTrailURL);
$.ajax({
url: auditTrailURL,
method: "GET",
headers: {"Accept": "application/json; odata=verbose"},
success: function (data) {
var items = data.d.results;
for (var i = 0; i < items.length; i++) {
var creation_date = items[i].Created;
var current_user = items[i].User_x0020_Name;
console.log(items[i]);
$('#AuditTrailTable_Financial').append('<tr class="audit_content">' +
'<td align=center> ' + format_date(creation_date) + ' </td>' +
'<td align=center> ' + items[i].WorkflowStageShort+ ' </td>' +
'<td align=center> ' + items[i].Author.Title + ' </td>' +
'<td align=center> ' + items[i].Source + ' </td>' +
'<td align=center> ' + items[i].Previous_Total_Most_Likely_Forec + ' </td>' +
'<td align=center> ' + items[i].Submitted_Total_Most_Likely_Fore + ' </td>' +
'</tr>');
}
$('.audit_content').hide();
console.log(data);
},
error: function (data) { alert("Some error occurred in getting Audit Trail")}
});
}

//apply a filter on data.d.result array
var items = data.d.results.filter(function (el, i, arr) {
//if index match then this element is keeped
//arr is the array on which filter was called (is data.d.result)
return i === arr.findIndex(function (obj) {
//need to satisfies this conditions
return obj.User_x0020_Name === el.User_x0020_Name &&
obj.Previous_Total_Most_Likely_Forec === el.Previous_Total_Most_Likely_Forec &&
obj.Submitted_Total_Most_Likely_Fore === el.Submitted_Total_Most_Likely_Fore &&
obj.Created >= el.Created;
})
});
reference:
Array.prototype.filter()
Array.prototype.findIndex()

Maybe you can do this using a hash map to store index location of the unique triplet in order to replace it when we find a newer one.
// This will be our final result array, with no duplicates.
const result = []
// Here we will store the result index location of a stored item.
const latest_map = new Map()
// build a unique key for the triplet combo.
function buildKey(item) {
const separator = "__"
return item.User_x0020_Name + separator + item.Previous_Total_Most_Likely_Forec + separator + item.Submitted_Total_Most_Likely_Fore
}
// check if item exist, replace
function deduplicateAndKeepLatest(item) {
const key = buildKey(item);
if (latest_map.has(key)) {
result[latest_map.get(key)] = item; // replace old item with latest.
} else {
result.push(item);
latest_map.set(key, result.length-1) // first time seeing this item, save the index it was stored at.
}
}
// make sure items is in the correct sort order, since you want to keep the latest.
items.forEach((item) => {
deduplicateAndKeepLatest(item);
}
return result; // should contain unique triplet, and only latest one.

Thank you everyone for your replies.
I have sorted the table in descending order and used the below code to solve the issue:
function (data) {
var items = data.d.results;
var current_user = "";
var current_Previous_Total = 0;
var current_Submitted_Total = 0;
for (var i = 0; i < items.length; i++) {
var creation_date = items[i].Created;
if (current_user != items[i].User_x0020_Name || current_Previous_Total != items[i].Previous_Total_Most_Likely_Forec || current_Submitted_Total != items[i].Submitted_Total_Most_Likely_Fore) {
current_user = items[i].User_x0020_Name;
current_Previous_Total = items[i].Previous_Total_Most_Likely_Forec;
current_Submitted_Total = items[i].Submitted_Total_Most_Likely_Fore;
$('#AuditTrailTable_Financial').append('<tr class="audit_content">' +
'<td id="tdDate" align=center> ' + format_date(creation_date) + ' </td>' +
'<td id="tdStage" align=center> ' + items[i].WorkflowStageShort+ ' </td>' +
'<td id="tdUser" align=center> ' + items[i].Author.Title + ' </td>' +
'<td id="tdSource" align=center> ' + items[i].Source + ' </td>' +
'<td id="tdPrevious" align=center> ' + items[i].Previous_Total_Most_Likely_Forec + ' </td>' +
'<td id="tdSubmitted" align=center> ' + items[i].Submitted_Total_Most_Likely_Fore + ' </td>' +
'</tr>');
}
}
$('.audit_content').hide();
console.log(data);
},

Related

how to check if a html table has duplicated rows in js and sum inputs instead of insert another row?

I'm learning JS while doing a POS System, and I'm having a hard time trying to figure out how to check if the product added is already scanned before inserting and if so, change the quantity input instead.
So far when I scan the product id it inserts without a problem, but when I scan the same id it inserts in a new row. It seem that my function comprobacion isn't working. I tried with other using a for to search in the rows, and I tried some solutions that I found online but nothing seems to work.
here is an example of what its happening
https://gfycat.com/respectfultemptingeastrussiancoursinghounds
idProductos is the primary key and is hidden in the rows, so im introducing codigo (it's another unique column, both cannot be null).
Can someone help me? I'm lost.
This is my code
$.ajax({
method: "POST",
url: "../php/venta.php",
data: param,
success: function(data) {
if (data != null) {
var idProductos,
Codigo,
nombre,
precioVenta;
// console.log(data);
var rows = jQuery.parseJSON(data);
idProductos = rows[0].idProductos;
Codigo = rows[0].Codigo;
nombre = rows[0].nombre;
precioVenta = rows[0].precioVenta;
(idProductos)
if (comprobacion(idProductos) == false) {
var nuevoValor = $(parseInt($('.inputCantidad')[i]).val()) + 1;
$($('.inputCantidad')[i]).val(nuevoValor);
var valorImporte = $($('.inputprecioVenta')[i]).val() * nuevoValor;
$($('.inputImporte')[i]).val(valorImporte);
} else {
var table = document.getElementById('tablaVenta');
var newRow = document.createElement("tr");
newRow.align = "center";
var contentRow =
'<td><input type="hidden" class="inputId" value="' + idProductos + '">' + Codigo + '</td>' +
'<td>' + nombre + '</td>' +
'<td><input class="inputprecioVenta" value="' + precioVenta + '"></td>' +
'<td><input class="inputCantidad" value="1"></td>' +
'<td><input class="inputImporte" value="' + precioVenta + '"></td>';
newRow.innerHTML = contentRow;
table.appendChild(newRow);
}
}
},
error: function(jqXHR, textStatus, errorThrown) { //errores
alert(jqXHR + textStatus + errorThrown);
},
})
}
the function comprobacion
function comprobacion(idProductos) {
var id = $(idProductos).val();
$('tbody tr').each(function() {
if ($(this).val() == id) {
return false;
}
});
return true;
}
I would add the id to the row using a custom data attribute, like data-id, and use that, along with some clever selector creation to quickly identify if the id has been used before.
$.ajax({
method: "POST",
url: "../php/venta.php",
data: param,
success: function(data) {
if (data != null) {
var idProductos,
Codigo,
nombre,
precioVenta;
// console.log(data);
var rows = jQuery.parseJSON(data);
idProductos = rows[0].idProductos;
Codigo = rows[0].Codigo;
nombre = rows[0].nombre;
precioVenta = rows[0].precioVenta;
(idProductos)
if (comprobacion(idProductos) == false) {
var nuevoValor = $(parseInt($('.inputCantidad')[i]).val()) + 1;
$($('.inputCantidad')[i]).val(nuevoValor);
var valorImporte = $($('.inputprecioVenta')[i]).val() * nuevoValor;
$($('.inputImporte')[i]).val(valorImporte);
} else {
var table = document.getElementById('tablaVenta');
var newRow = document.createElement("tr");
newRow.align = "center";
/* Add the line below */
newRow.setAttribute("data-id", idProductos);
var contentRow =
'<td><input type="hidden" class="inputId" value="' + idProductos + '">' + Codigo + '</td>' +
'<td>' + nombre + '</td>' +
'<td><input class="inputprecioVenta" value="' + precioVenta + '"></td>' +
'<td><input class="inputCantidad" value="1"></td>' +
'<td><input class="inputImporte" value="' + precioVenta + '"></td>';
newRow.innerHTML = contentRow;
table.appendChild(newRow);
}
}
},
error: function(jqXHR, textStatus, errorThrown) { //errores
alert(jqXHR + textStatus + errorThrown);
},
})
Then, the comprobacion function becomes easier:
function comprobacion(idProductos) {
return $('tbody tr[data-id="' + idProductos + '"]').length === 0;
}
Set id to HTML inputs, is more quick to find ProductID with JS.
'<td><input type="hidden" id="hid_' + idProductos + '" class="inputId" value="' + idProductos + '">' + Codigo + '</td>' +
'<td>' + nombre + '</td>' +
'<td><input id="hid_' + idProductos + '" class="inputprecioVenta" value="' + precioVenta + '"></td>' +
'<td><input id="qty_' + idProductos + '" class="inputCantidad" value="1"></td>' +
'<td><input id="cst_' + idProductos + '" class="inputImporte" value="' + precioVenta + '"></td>';
Try $('tbody tr td').each(function().
The value is in the td, not the tr

How to combine multiple functions into one without repeating?

In the ProdRender.js I wanna combine those three functions into one so that i do not repeat and that should match to ProdData.js as the data is in the ProdData.js and its rendering through ProdRender.js
Could someone please suggest me how to do it without repeating anything in the prodRender.js The ProdData.js seems to be working fine as i'm not repeating anything only the prodRender.js is where i'm repeating thrice.
So please help me out here
Thanks
//ProdRender.js
function ProductDataRenderer() { }
ProductDataRenderer.prototype.render = function () {
var nzd =
'<table class="table table-striped">'
+' <thead>'
+' <tr><td colspan="3">Products (NZD)</td></tr>'
+' <tr>'
+' <td>Name</td>'
+' <td>Price</td>'
+' <td>Type</td>'
+' </tr>'
+' </thead>'
+ ' <tbody>';
var n = ProductDataConsolidator.get();
for (var i = 0; i < n.length; i++) {
nzd +=
'<tr>'
+ '<td>' + n[i].name +'</td>'
+ '<td>' + n[i].price + '</td>'
+ '<td>' + n[i].type + '</td>'
+ '</tr>';
}
nzd += '</tbody></table>';
document.getElementById("nzdProducts").innerHTML = nzd;
var usd =
'<table class="table table-striped">'
+ ' <thead>'
+ ' <tr><td colspan="3">Products (USD)</td></tr>'
+ ' <tr>'
+ ' <td>Name</td>'
+ ' <td>Price</td>'
+ ' <td>Type</td>'
+ ' </tr>'
+ ' </thead>'
+ ' <tbody>';
var u = ProductDataConsolidator.getInUSDollars();
for (var i = 0; i < u.length; i++) {
usd +=
'<tr>'
+ '<td>' + u[i].name + '</td>'
+ '<td>' + u[i].price + '</td>'
+ '<td>' + u[i].type + '</td>'
+ '</tr>';
}
usd += '</tbody></table>';
document.getElementById("usdProducts").innerHTML = usd;
var euro =
'<table class="table table-striped">'
+ ' <thead>'
+ ' <tr><td colspan="3">Products (Euro)</td></tr>'
+ ' <tr>'
+ ' <td>Name</td>'
+ ' <td>Price</td>'
+ ' <td>Type</td>'
+ ' </tr>'
+ ' </thead>'
+ ' <tbody>';
var e = ProductDataConsolidator.getInEuros();
for (var i = 0; i < e.length; i++) {
euro +=
'<tr>'
+ '<td>' + e[i].name + '</td>'
+ '<td>' + e[i].price + '</td>'
+ '<td>' + e[i].type + '</td>'
+ '</tr>';
}
euro += '</tbody></table>';
document.getElementById("euProducts").innerHTML = euro;
}
//ProdData.js
function ProductDataConsolidator() { }
ProductDataConsolidator.get = function (currency) {
var l = new LawnmowerRepository().getAll();
var p = new PhoneCaseRepository().getAll();
var t = new TShirtRepository().getAll();
const arr_names = [
[l, "lawnmower"],
[p, "Phone Case"],
[t, "T-Shirt"],
]
var products = [];
let multiplier = currency == "euro"
? 0.67
: currency == "dollar"
? 0.76
: 1;
for (let [arr,name] of arr_names){
for (var i = 0; i < arr.length; i++) {
products.push({
id: arr[i].id,
name: arr[i].name,
price: (arr[i].price * multiplier).toFixed(2),
type: name
});
}
}
return products;
}
ProductDataConsolidator.getInEuros = function(){
return ProductDataConsolidator.get("euro");
}
ProductDataConsolidator.getInUSDollars = function(){
return ProductDataConsolidator.get("dollar");
}
You need to break it down to smaller functions and parameterise them
const table = (currency, content) =>
`<table class="table table-striped">
<thead>
<tr><td colspan="3">Products (${currency})</td></tr>
<tr>
<td>Name</td>
<td>Price</td>
<td>Type</td>
</tr>
</thead>
<tbody>
${content}
</tbody>
</table>`
;
const table_content = data =>
data.map(({ name, price, type }) =>
`<tr>
<td>${name}</td>
<td>${price}</td>
<td>${type}</td>
</tr>`)
.join('\n')
;
const currencyCode = {
dollar: 'USD',
euro: 'Euro',
newZealand: 'NZD'
};
function ProductDataRenderer() { }
ProductDataRenderer.prototype.render = function (currency, target) {
const productData = ProductDataConsolidator.get(currency);
const html = table(currencyCode[currency], table_content(productData));
document.getElementById(target).innerHTML = html;
}
I didn't change the design of your code but you can see render does 3 different things. It should only render, not also retrieve data and inject the table in the DOM.
It makes also little sense to have one ProductDataConsolidator with three static methods having different names. Either you create 3 derivatives of ProductDataConsolidator with only one method get each and you pass an instance of the right derivative to render so that it only needs to know about one method named get (by the way if you have one object with only one method it's a function so why bother use an object), or you pass the product data directly to render (preferred)

how to create a hierarchy table with parent child rows razor

here is my table snap enter image description here
I am creating this table from my model in razor view
it shows the structure of task and sub-tasks and their subtask ...
but the problem is it loads sub task and their subtask ... in the same level when someone clicks on the first column it loads its child under the parent
it's loads them and add a table row under the correspondence row
here is my jquery code I want to make it hierarchical like there should be a difference in parent and child level
function showHierarchy(taskId) {
if ($('.subtaskof_' + taskId).text() == '') {
$.ajax('/Tasks/sfsubtasks?taskId=' + taskId, // request url
{
async: false,
success: function (data, status, xhr) {// success callback function
var subtasklist = JSON.parse(data)
console.log(subtasklist);
for (i = 0; i < subtasklist.length; i++) {
subtask = subtasklist[i];
var therowis = '<tr class=subtaskof_' + taskId + '>'
+ '<td id="subtaskrow_' + subtask['InstanceId'] + '" align="right">_</td>'
+ '<td>' + subtask['InstanceId'] + '</td>'
+ '<td>' + subtask["Title"] + '</td>'
+ '<td>' + subtask["Deliverables"] + '</td>'
+ '<td>' + subtask["StartDate"] + '</td>'
+ '<td>' + subtask["Priority"] + '</td>'
+ '<td>' + subtask["State"] + '</td>'
+ '<td>See Details_subt</td>'
+ '<td>Add Sub Task_subt</td>'
+ '</tr>'
// Find position to add new subtask row in the Task table
$("#my-grid tr").filter(function () {
if ($(this).text().indexOf(taskId) >= 0) {
$(this).after(therowis);
issubsubtaskexists = false;
console.log("chield checking for - " + subtask['InstanceId'])
$.ajax('/Tasks/sfsubtasks?taskId=' + subtask['InstanceId'], // request url
{
async: false,
success: function (data_, status_, xhr_) {
if (data_.length > 0) {
console.log("The data_ is - " + data_);
var subsubtasklist = JSON.parse(data_);
console.log("The subsubtasklist is - " + subsubtasklist)
console.log("lenght for - " + subtask['InstanceId'] + " , is - " + subsubtasklist);
if (subsubtasklist.length > 0) {
$('#subtaskrow_' + subtask['InstanceId']).html("<b><a style='font-size:25px; padding-left:17px;' id='lnk_" + subtask['InstanceId'] + "' href='#' onclick='showHierarchy(" + subtask['InstanceId'] + ")'> + </a></b>")
issubsubtaskexists = true;
}
}
}
});
console.log("The taskId is - "+taskId)
$('#lnk_' + taskId).html('<b>_</b>');
}
});
}
}
});
} else {
// Toggle/removing subtasks
$('.subtaskof_' + taskId).remove();
$.ajax('/Tasks/sfsubtasks?taskId=' + taskId,
{
success: function (data, status, xhr) {
console.log("Checking for child node of taskId - " + taskId);
var subsubtasklist = JSON.parse(data)
console.log(subsubtasklist);
for (i = 0; i < subsubtasklist.length; i++) {
$('.subtaskof_' + subsubtasklist[i]['InstanceId']).remove();
$.ajax('/Tasks/sfsubtasks?taskId=' + subsubtasklist[i],
{
success: function (data, status, xhr) {
console.log("Checking for child node of taskId - " + taskId);
var subsubtasklist_ = JSON.parse(data)
console.log(subsubtasklist_);
for (j = 0; j < subsubtasklist_.length; j++) {
$('.subtaskof_' + subsubtasklist_[j]['InstanceId']).remove();
}
}
});
}
}
});
$('#lnk_' + taskId).html('<b>+</b>');
}
}
plz let me know what can be done of this table for showing data hierarchically

Depend on the popup's table data, parent's table data showing item is different

I have a popup modal like this one.
When I click 'ADD' button, all the data from popup's table is shown at the table of the parent's. Like this one.
The problem is that I don't want to show the plus sign "+", if there is no data in textbox2s.
Here is the code at popup.js
function add_to_prent_table(){
var popupTable = [];
var i = 0;
$('#testing > tbody > tr').each(function () {
popupTable[i] = [
$(this).find("#test_number").val(),
$(this).find("#type_1").val(),
$(this).find("#type_2").val(),
$(this).find("#place_1").val(),
$(this).find("#place_2").val(),
];
i++;
var newRow = '<tr>'+
'<td id ="td_center">'+
$(this).find("#test_piece_number").val() +
'</td>'+
'<td id ="td_center">'+
$(this).find("#type_1").val() + ' + ' +
$(this).find("#type_2").val() +
'</td>'+
'<td id ="td_center">'+
$(this).find("#place_1").val() + ' + ' +
$(this).find("#place_2").val() +
'</td>'+
'</tr>';
$('#testing_parent tbody').append(newRow);
});
}
How can I fix this?
It's messy but you can replace the first ' + ' with this:
$(this).find("#type_2").val() ? ' + ' : ''
And replace the second ' + ' with
$(this).find("#place_2").val() ? ' + ' : ''
Basically you're looking to see if #type_2 and #place_2 have values. If they do, add a ' + '. If not, add nothing.
Try this;
function add_to_prent_table() {
var popupTable = [];
var i = 0;
$('#testing > tbody > tr').each(function () {
var testNumber = $(this).find("#test_number").val();
var firstType = $(this).find("#type_1").val();
var secondType = $(this).find("#type_2").val();
var firstPlace = $(this).find("#place_1").val();
var secondPlace = $(this).find("#place_2").val();
popupTable[i] = [
testNumber,
firstType,
secondType,
firstPlace,
secondPlace,
];
i++;
var newRow = '<tr>' +
'<td id ="td_center">' +
$(this).find("#test_piece_number").val() +
'</td>' +
'<td id ="td_center">' +
firstType + secondType ? (' + ' + secondType) : '' +
'</td>' +
'<td id ="td_center">' +
firstPlace + secondPlace ? (' + ' + secondPlace) : '' +
'</td>' +
'</tr>';
$('#testing_parent tbody').append(newRow);
});
}
Simply you can add condition before adding plus sign like below,
var newRow = '<tr>'+
'<td id ="td_center">'+
$(this).find("#test_piece_number").val() +
'</td>'+
'<td id ="td_center">'+
$(this).find("#type_1").val()
if($(this).find("#type_2").val() != "")
{
' + ' + $(this).find("#type_2").val()
}
'</td>'+
'<td id ="td_center">'+
$(this).find("#place_1").val()
if($(this).find("#place_2").val() != "")
{
' + ' + $(this).find("#place_2").val()
}
'</td>'+
'</tr>';

Change color related on value

I have table values populated from back-end
Here is js function that doing it.
function AllProposals() {
let getProposalsUrl = '/proposals/index';
$.ajax({
url: getProposalsUrl,
contentType: 'application/json; charset=utf-8',
type: 'GET',
dataType: 'json',
processData: false,
success: function (data) {
$("#proposals").empty();
var list = data;
for (var i = 0; i <= list.length - 1; i++) {
var tableData = '<tr>' +
'<td class="proposalId">' +
list[i].Id +
'</td>' +
'<td > ' +
list[i].Project +
'</td>' +
'<td > ' +
moment(list[i].DateFrom).format('DD/MM/YYYY') + "--" + moment(list[i].DateTo).format('DD/MM/YYYY') +
'</td>' +
'<td> ' +
list[i].WorkTime + "--" +list[i].WorkTimeTo +
'</td>' +
'<td > ' +
list[i].Quantity+
'</td>' +
'<td> ' +
list[i].Service +
'</td>' +
'<td> ' +
list[i].Price +
'</td>' +
'<td> ' +
list[i].Status +
'</td>' +
'</tr>';
$('#proposals').append(tableData);
}
}
})
}
It working great.
Bu It need to check this value on flight
'<td> '+list[i].Status+'</td>' +
And if it is "Rejected" change text color to red.
How I can do this correctly?
Thank's for help.
Assuming that this code will need some refactoring if you will need to reuse the return data of the ajax call and in general it is not good looking, I would do as follows:
'<td'+ (list[i].Status == 'Rejected' ? ' style="color:red;"' : '') +'> ' +
list[i].Status +
'</td>' +
Edit
If in future you will need to assign different colors based on the content of list[i].Status, I suggest to create a content-to-color lookup table:
let contentToColor = {
"Rejected": "red",
"Success": "green",
"Warning": "yellow"
};
and then:
'<td'+ (contentToColor[list[i].Status] !== 'undefined' ? ' style="color: '+ contentToColor[list[i].Status] +';"' : '') +'> ' +
list[i].Status +
'</td>' +
The way of checking the existence of the variable may be wrong, I don't remember how it is done in JS, but you get the concept.
Anyway, I would suggest to refactor the code by separating the presentation code and the domain code. You will save yourself by the ugly code I wrote above. I had to read it 10 times for checking if the quotes were good.
You can use a switch to get the status and set the color base on what you get and pass it to a variable.
Example
<script>
function AllProposals() {
let getProposalsUrl = '/proposals/index';
$.ajax({
url: getProposalsUrl,
contentType: 'application/json; charset=utf-8',
type: 'GET',
dataType: 'json',
processData: false,
success: function (data) {
$("#proposals").empty();
var list = data;
for (var i = 0; i <= list.length - 1; i++) {
var mycolor = "";
switch (list[i].Status) {
case "Approved":
mycolor = "style="color:green";
break;
case "Rejected":
mycolor = "style="color:red";
//Add more if needed
}
var tableData = '<tr>' +
'<td class="proposalId">' +
list[i].Id +
'</td>' +
'<td > ' +
list[i].Project +
'</td>' +
'<td > ' +
moment(list[i].DateFrom).format('DD/MM/YYYY') + "--" + moment(list[i].DateTo).format('DD/MM/YYYY') +
'</td>' +
'<td> ' +
list[i].WorkTime + "--" +list[i].WorkTimeTo +
'</td>' +
'<td > ' +
list[i].Quantity+
'</td>' +
'<td> ' +
list[i].Service +
'</td>' +
'<td> ' +
list[i].Price +
'</td>' +
'<td' + mycolor +'> ' +
list[i].Status +
'</td>' +
'</tr>';
$('#proposals').append(tableData);
}
}
})
}
</script>
You can use alter the style attribute using jQuery's .attr method (http://api.jquery.com/attr/)
if(status=="rejected"){
$(.elementclass).attr("style","color:red");
}

Categories