I'm using the DataTables jQuery plugin.
I want to search the table if a term shown in at least one of two specific columns.
The current code below only finds rows where the word "word" is in both columns. I need to find rows where the word is in either of the columns.
$table.DataTable().columns([0,1]).search("word");
I've considered using global search and setting searchable option of other columns to false, but I couldn't find a way to change this option at runtime.
Search all columns
You can use regular expressions when searching a table.
For example, the code below shows search results containing either word Angelica or London in all columns.
var table = $('#example').DataTable();
table
.search('Angelica|London', true, false)
.draw();
See this jsFiddle for code and demonstration.
Search specific columns
To search specific columns you may need to utilize custom search functionality.
The code below shows search results containing either word Angelica or Tokyo in table data values with indexes 0, 1, and 2.
var table = $('#example').DataTable();
var terms = ['Angelica', 'Tokyo'];
// Convert terms to lower case
$.each(terms, function(index, value){
terms[index] = value.toLowerCase();
});
// Enable custom search
$.fn.dataTable.ext.search.push(
function (settings, data, dataIndex) {
var isFound = false;
$.each(data, function (index, value){
// Evaluate only first three data values
if(index === 0 || index === 1 || index === 2){
// Convert data to lower case
value = value.toLowerCase();
$.each(terms, function(termIndex, termValue){
// If data contains the term
if (value.indexOf(termValue) !== -1) {
isFound = true;
}
return !isFound;
});
}
return !isFound;
});
return isFound;
}
);
// Perform search
table.draw();
// Disable custom search
$.fn.dataTable.ext.search.pop();
See this jsFiddle for code and demonstration.
Related
Because of the issues Google Sheets has with the ImportRange function, I wanted to create an AppScript to replace the ImportRange. I am new into JS but currently, I have the following:
function My_ImportRange() {
var clearContent = SpreadsheetApp.getActive().getSheetByName("Sheet 1").getDataRange().clearContent()
var values = SpreadsheetApp.openById('sheet-id').
getSheetByName('name').getRange('A:AI').getValues();
// filtering data
var filtered_values = values.filter(filterlogic)
SpreadsheetApp.getActive().getSheetByName('Sheet 1').
getRange(2,1,filtered_values.length,filtered_values[0].length).setValues(filtered_values);
}
var filterlogic = function(item){
return item[0] === "filter 1" &&
item[6] === "filter 2";};
The issue I have is that when I filter the table (with the filterlogic function), the first row or column names get dropped off because of the filter .
How can I tell the filter to only run the logic from the 2nd row, ignoring the column names?
Thank you in advance!
Use the script below.
Remove the header using shift and add it back after filtering using unshift
// assuming data is in Sheet1 A:H
var values = SpreadsheetApp.getActive().getSheetByName("Sheet1").getRange('A:H').getValues();
// remove headers before filtering
var headers = values.shift();
// filter values
var filtered_values = values.filter(filterlogic)
// append back the headers in the first row after filtering
filtered_values.unshift(headers);
Logger.log(filtered_values)
OR tell the filter to exclude index 0 which should be the first row.
If we filter "A" and 10 for column A and G:
var filterlogic = function(item, index){
// return true IF condition is met OR index is 0 or the first row.
return (item[0] === "A" && item[6] === 10) || index == 0;
};
Both of modification will exclude the 1st element in values which is the 1st row. Choose what you think is best. Both will return the same data.
I'm trying to implement conditional drop-down lists in the sheet you see below using Google Script (if you have any other ideas, though, feel free to share). I based most of my code on this tutorial. The idea is to update the possible list values on the third column ("Indicador"), based on the value selected on the second column of each corresponding row.
The correspondence table used to determine what list of values should be used can be found in Figure 2 (another worksheet). Note that the values that are searched for are on the first column and possibly returned lists on the last column of the table displayed in figure 2 (it's, in essence, a VLOOKUP).
Figure 1 - Sheet where drop-down will be added
So far, the code looks like this:
function getSpecVars() {
var ws_inds = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Indicadores");
var ws_support = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Apoio");
var range_valores = ws_support.getRange("A5:A9").getValues();
var range_listas = ws_support.getRange("G5:G9").getValues();
Logger.log(range_listas);
}
// Updates the value on a cell in the third column corresponding to the updated value in the same row in another column.
function onEdit(e) {
var active_cell = e.range;
var val = active_cell.getValue();
var row_alter = active_cell.getRow();
var col_alter = active_cell.getColumn();
var row_list = range_listas.indexOf(val);
var list = range_listas[row_list];
var alt_cell = ws_ind.getRange(row_alter,3)
if(col_alter === 2){
applyValidation(list,alt_cell);
}
}
function applyValidation(list, cell) {
var rule = SpreadsheetApp
.newDataValidation()
.requireValueInList(list)
.setAllowInvalid(false)
.build();
cell.setDataValidation(rule);
}
Notice that:
The first function saves the lists from the support sheet (see Figure 2, below) in arrays for later use;
The second function actually implements the Data Validation, drawing from the variables stored in the first function and the third function.
The third function is a generic function for setting a Data Validation list in any given cell.
Figure 2 - Support sheet with lists to be added to drop-down options
What I expected to see:
I expected the cells on the third column of the first sheet (Figure 1, above) to only allow input from a drop-down list filtered/chosen according to the name of the category on the second column. In other words, I expect the OnEdit script to implement a Data Validation list on the third column whenever I modify a value on the second column.
What I am getting:
Nothing, really. It does nothing. Any ideas?
function get_range_listas() {
return SpreadsheetApp.getActiveSpreadsheet()
.getSheetByName("Apoio")
.getRange("A5:A9")
.getValues() // <- getValues returns a 2d Array
.map(function (r) {
return r[0];
}); // <- unwrap to Array
}
function get_range_valores() {
return SpreadsheetApp.getActiveSpreadsheet()
.getSheetByName("Apoio")
.getRange("G5:G9")
.getValues() // <- getValues returns a 2d Array
.map(function (r) {
return r[0];
}); // <- unwrap to Array
}
/*
* Updates the value on a cell in the third column corresponding to
* the updated value in the same row in another column.
*/
function onEdit(e) {
if (
e.source.getActiveSheet().getName() !== "Indicadores" ||
e.range.columnStart !== 2
) { return; }
var active_cell = e.range;
var val = e.value;
var row_list = get_range_listas().indexOf(val);
var list = get_range_valores()[row_list].split(",");
var rule = SpreadsheetApp.newDataValidation()
.setAllowInvalid(false)
.requireValueInList(list, true)
.build();
e.range.offset(0, 1).setDataValidation(rule);
}
I have a UI application to display business information. I have used slick grid to show all business data in tabular/grid structure by using all built-in properties of slick grid. I have used filter property to filter data on each column.
But I have a condition that i am getting 1k data from the Azure storage table and i set the pageSize to 10 and i pass the pageSize to set the pagination in dataView.setPagingOptions({pageSize:pageSize}); When we click on slick-header-menuitem in dropdown it displays (Sort Ascending,Sort Descending,Search and data whatever grid containing on the (filter div) ).
Now once I click on the checkbox to filter the data whatever the data i need to filter it will filter those data fine without any issue.Uptill here every thing is working as expected as here in my filter dropdown i have 4 option like (Select All , Blank , IT ,U.S.A ,USA ) which is shown in the image below.
But the problem starts form here once i again click on the slick-header-menuitem after the filteration dropdown (filter div) display few more data like
(Select All , Blank , IT ,U.S.A ,USA ,UNITED STATES,US) because i have filtered data suppose (IT) so in grid all the data filter and get the data from the 1k records but it will also get the data appended in the filter div options which was not there in previously.
for refrence my grid is looking someting like this (below is the url) except search and pagination
http://danny-sg.github.io/slickgrid-spreadsheet-plugins/examples/example-2-everything.htm
I am also attaching two images
first image indicate when i click on the first time on slick-header-menuitem dropdown.
And second image indicate when i again click on the slick-header-menuitem dropdown after the filtered data.
I have gone through the slickgrid library in which there is a plugin folder this folder contain filter folder and filter folder contain ext.headerfilter.js
this file contain method called "function getFilterValues(dataView, column){...}", "function getFilterValuesByInput($input){...}" and "function getAllFilterValues(data, column){...}", i have debug it but won't get any success.
finally a lots of debugging i got the solution for filtering the records
here i am not bothering about the slick-grid library,it is an awsome library as per my experience
so on the basis of my requirement i have done some changes in ext.headerfilter.js file
go to the function called getFilterValues() and getFilterValuesByInput($input), inside these function i have done some changes in for loop and variable below is the code has been implemented for getFilterValues() and getFilterValuesByInput($input)
function getFilterValues(dataView, column) {
var seen = [];
// for (var i = 0; i < dataView.getLength() ; i++) {
for (var i = 0; i < dataView.getItems().length ; i++) {
// var value = dataView.getItem(i)[column.field];
var value = dataView.getItems()[i][column.field];
if (!_.contains(seen, value)) {
seen.push(value);
}
}
return _.sortBy(seen, function (v) { return v; });
}
code for getFilterValuesByInput($input)
function getFilterValuesByInput($input) {
var column = $input.data("column"),
filter = $input.val(),
dataView = grid.getData(),
seen = [];
// for (var i = 0; i < dataView.getLength() ; i++) {
for (var i = 0; i < dataView.getItems().length ; i++) {
// var value = dataView.getItem(i)[column.field];
var value = dataView.getItems()[i][column.field];
if (filter.length > 0) {
var mVal = !value ? '' : value;
var lowercaseFilter = filter.toString().toLowerCase();
var lowercaseVal = mVal.toString().toLowerCase();
if (!_.contains(seen, value) && lowercaseVal.indexOf(lowercaseFilter) > -1) {
seen.push(value);
}
}
else {
if (!_.contains(seen, value)) {
seen.push(value);
}
}
}
return _.sortBy(seen, function (v) { return v; });
}
I have two worksheets in my google spreadsheet:
Input data is coming into the Get Data worksheet via the importxml function.
However, I would like to copy all values of the Get Data sheet to the Final Data sheet and if there are duplicates(in terms of rows) append the unique row.
Here is what I tried:
function onEdit() {
//get the data from old Spreadsheet
var ss = SpreadsheetApp.openById("1bm2ia--F2b0495iTJotp4Kv1QAW-wGUGDUROwM9B-D0");
var dataRange = ss.getSheetByName("Get Data").getRange(1, 1, ss.getLastRow(), ss.getLastColumn());
var dataRangeFinalData = ss.getSheetByName("Final Data").getRange(1, 1, ss.getLastRow(), ss.getLastColumn());
var myData = dataRange.getValues();
//Open new Spreadsheet & paste the data
newSS = SpreadsheetApp.openById("1bm2ia--F2b0495iTJotp4Kv1QAW-wGUGDUROwM9B-D0");
Logger.log(newSS.getLastRow());
newSS.getSheetByName("Final Data").getRange(newSS.getLastRow()+1, 1, ss.getLastRow(), ss.getLastColumn()).setValues(myData);
//remove duplicates in the new sheet
removeDups(dataRangeFinalData)
}
function getId() {
Browser.msgBox('Spreadsheet key: ' + SpreadsheetApp.getActiveSpreadsheet().getId());
}
function removeDups(array) {
var outArray = [];
array.sort(lowerCase);
function lowerCase(a,b){
return a.toLowerCase()>b.toLowerCase() ? 1 : -1;// sort function that does not "see" letter case
}
outArray.push(array[0]);
for(var n in array){
Logger.log(outArray[outArray.length-1]+' = '+array[n]+' ?');
if(outArray[outArray.length-1].toLowerCase()!=array[n].toLowerCase()){
outArray.push(array[n]);
}
}
return outArray;
}
Below you can find the link to a sample spreadsheet:
Sample Sheet
My problem is that the data does not get pasted.
I appreciate your replies!
tl;dr: See script at bottom.
An onEdit() function is inappropriate for your use case, as cell contents modified by spreadsheet functions are not considered "edit" events. You can read more about that in this answer. If you want this to be automated, then a timed trigger function would be appropriate. Alternatively, you could manually invoke the function by a menu item, say. I'll leave that to you to decide, as the real meat of your problem is how to ensure row-level uniqueness in your final data set.
Merging unique rows
Although your original code is incomplete, it appears you were intending to first remove duplicates from the source data, utilizing case-insensitive string comparisons. I'll suggest instead that some other JavaScript magic would help here.
We're interested in uniqueness in our destination data, so we need to have a way to compare new rows to what we already have. If we had arrays of strings or numbers, then we could just use the techniques in How to merge two arrays in Javascript and de-duplicate items. However, there's a complication here, because we have an array of arrays, and arrays cannot be directly compared.
Hash
Fine - we could still compare rows element-by-element, which would require a simple loop over all columns in the rows we were comparing. Simple, but slow, what we would call an O(n2) solution (Order n-squared). As the number of rows to compare increased, the number of unique comparison operations would increase exponentially. So, let's not do that.
Instead, we'll create a separate data structure that mirrors our destination data but is very efficient for comparisons, a hash.
In JavaScript we can quickly access the properties of an object by their name, or key. Further, that key can be any string. We can create a simple hash table then, with an object whose properties are named using strings generated from the rows of our destination data. For example, this would create a hash object, then add the array row to it:
var destHash = {};
destHash[row.join('')] = true; // could be anything
To create our key, we're joining all the values in the row array with no separator. Now, to test for uniqueness of a row, we just check for existence of an object property with an identically-formed key. Like this:
var alreadyExists = destHash.hasOwnProperty(row.join(''));
One additional consideration: since the source data can conceivably contain duplicate rows that aren't yet in the destination data, we need to continuously expand the hash table as unique rows are identified.
Filter & Concatenate
JavaScript provides two built-in array methods that we'll use to filter out known rows, and concatenate only unique rows to our destination data.
In its simple form, that would look like this:
// Concatentate source rows to dest rows if they satisfy a uniqueness filter
var mergedData = destData.concat(sourceData.filter(function (row) {
// Return true if given row is unique
}));
You can read that as "create an array named mergedData that consists of the current contents of the array named destData, with filtered rows of the sourceData array concatenated to it."
You'll find in the final function that it's a little more complex due to the other considerations already mentioned.
Update spreadsheet
Once we have our mergedData array, it just needs to be written into the destination Sheet.
Padding rows: The source data contains rows of inconsistent width, which will be a problem when calling setValues(), which expects all rows to be squared off. This will require that we examine and pad rows to avoid this sort of error:
Incorrect range width, was 6 but should be 5 (line ?, file "Code")
Padding rows is done by pushing blank "cells" at the end of the row array until it reaches the intended length.
for (var col=mergedData[row].length; col<mergedWidth; col++)
mergedData[row].push('');
With that taken care of for each row, we're finally ready to write out the result.
Final script
function appendUniqueRows() {
var ss = SpreadsheetApp.getActive();
var sourceSheet = ss.getSheetByName('Get Data');
var destSheet = ss.getSheetByName('Final Data');
var sourceData = sourceSheet.getDataRange().getValues();
var destData = destSheet.getDataRange().getValues();
// Check whether destination sheet is empty
if (destData.length === 1 && "" === destData[0].join('')) {
// Empty, so ignore the phantom row
destData = [];
}
// Generate hash for comparisons
var destHash = {};
destData.forEach(function(row) {
destHash[row.join('')] = true; // could be anything
});
// Concatentate source rows to dest rows if they satisfy a uniqueness filter
var mergedData = destData.concat(sourceData.filter(function (row) {
var hashedRow = row.join('');
if (!destHash.hasOwnProperty(hashedRow)) {
// This row is unique
destHash[hashedRow] = true; // Add to hash for future comparisons
return true; // filter -> true
}
return false; // not unique, filter -> false
}));
// Check whether two data sets were the same width
var sourceWidth = (sourceData.length > 0) ? sourceData[0].length : 0;
var destWidth = (destData.length > 0) ? destData[0].length : 0;
if (sourceWidth !== destWidth) {
// Pad out all columns for the new row
var mergedWidth = Math.max(sourceWidth,destWidth);
for (var row=0; row<mergedData.length; row++) {
for (var col=mergedData[row].length; col<mergedWidth; col++)
mergedData[row].push('');
}
}
// Write merged data to destination sheet
destSheet.getRange(1, 1, mergedData.length, mergedData[0].length)
.setValues(mergedData);
}
I'm trying to filter a table with some filters. Some are simple selects, and others are multiples. For the simple ones, that's ok, but not the multiple.
I want to follow this logic :
Passing through the array which contains the filter (filtre_transports)
Passing through the array which contains the value(s) (ligne_transports)
If an element of the 1. isn't in the 2. so not display the line (transports_matches = false)
I made this code :
// Pass through each line of the table
jQuery('#agents_liste tbody tr').not('.vide').each(function() {
var transports_matches = true;
// ligne_transports is an array contains values to compare with the filter
var ligne_transports = jQuery(this).children('td').eq(2).text().split('###');
// filtre_transports is an array contains the selected val of a multi select
jQuery(filtre_transports).each(function() {
var filtre = jQuery(this);
var filtreOk = false;
jQuery(ligne_transports).each(function() {
if (filtre == jQuery(this)) {
filtreOk = true;
return false;
}
});
if (!filtreOk) {
transports_matches = false;
return false;
}
});
});
The problem : When we have filters selected, the result transports_matches is always false.
Btw, I saw this post where the answer is to use classes, but is there a way without them ?
EDIT : You can see the JSFiddle here.
Thanks
Fixed: http://jsfiddle.net/r4mfv/2/
You had a couple of issues:
$(filtre_transports).each is not the way to iterate over an array, you should use $.each(filtre_transports, function() {...}).
You should cast filtre and this to String before comparing them.