I have created a table and sorted it with tablesorter. One of the columns is a mix of letters and numbers (chr1, chr2, ..., chr10, ... , chrM). I want this column to be sorted as if it were only numbers (from 1 to 22 and then X, Y and M in this order).
I have created my own parser and it works but just for some rows. Then, I find another block of rows which are correctly sorted followed by some other blocks. I do not know why this blocks are formed.
The code is here. Maybe with a smaller table it would work properly, because of that I have shown a big one.
Thanks in advance!
Try this parser (demo)
$.tablesorter.addParser({
// set a unique id
id: 'chrom',
is: function (s) {
// return false so this parser is not auto detected
return false;
},
format: function (s) {
// format your data for normalization
return s.toLowerCase()
.replace('chr', '')
.replace('x', '97')
.replace('y', '98')
.replace('m', '99');
},
// set type, either numeric or text
type: 'numeric'
});
Related
I have a scenario like Need to edit the single quotes values (only single quotes values),
So I extracted the single quotes values using regex and prepare the reactive dynamic form.
onclick of performing edit button will show old step name above, new step name below, submit step will replace the step name in the original array.
WOrking fine as expected in few scenarios according to my approach, but in scenarios, I realized whatever algorithm I am following does not fulfill my requirement.
Below are the test cases
Test case 1:
Step Name: "Then I should hire an employee using profile '1' for 'USA'",
// Here --> '1', 'USA' values are editable
Test case 2: "And Employee should be hired on '01' day of pay period '01' of 'Current' Fiscal"
// '01', '01', 'Current'
Issues: in test case 2 if I tried to edit second 01 it is editing the first 01
I try to solve the perform edit function with help of indexof, substring functions
this.replaceString = this.selectedStep.name;
this.metaArray.forEach((element: any) => {
var metaIndex = this.replaceString.indexOf(element.paramValue);
if (metaIndex !== -1) {
const replaceValue = this.stepEditForm.controls[element['paramIndex']].value;
this.replaceString = this.replaceString.substring(0, metaIndex) + replaceValue + this.replaceString.substring(metaIndex + (element.paramValue.length));
}
});
but in indexof always find the first occurrence of a value in a string. So I realized my approach is wrong on performed it function
please find the attachment for the code
https://stackblitz.com/edit/angular-reactive-forms-cqb9hy?file=app%2Fapp.component.ts
So Can anyone please suggest to me how to solve this issue,
Thanks in advance
I added a function called matchStartingPositions that returns the starting position indexes of each match. Using this method you can then perform your edit by replacing the string just as you do, but we'll find the proper match to be replaced at the given position.
So in your line
var metaIndex = this.replaceString.indexOf(element.paramValue);
we can then add a second parameter to indexOf, that is the starting point:
var metaIndex = this.replaceString.indexOf(element.paramValue, startingPositions[element.paramIndex]);
The function for getting the index positions just looks for those single quotes in a given string:
matchStartingPositions(str) {
let count = 0;
let indices = [];
[...str].forEach((val, i) => {
if (val === "'") {
if (count % 2 == 0) {
indices.push(i);
}
count++;
}
});
return indices;
}
Here it is in action:
https://angular-reactive-forms-xhkhmx.stackblitz.io
https://stackblitz.com/edit/angular-reactive-forms-xhkhmx?file=app/app.component.ts
I'm loading json file from database with two fields words and grade. Each word is graded for example true has 1 while lie has -1. Then i take input from text filed and i need to grade it based on grades from JSON file and then calculate score by summarizing the grades, but i just can't seem to find the way to do that. Words that are not in file are not being calculated.
I tried string.search match but it's to complicated and in the end i couldn't get result the way i wanted. I tried array searches same thing. I searched for on line solution, but no one has done anything similar so i can't copy it.
JSON
[
{"word":"true","grade":1},
{"word":"hate","grade":-1},
{"word":"dog","grade":0.8},
{"word":"cat","grade":-0.8}
]
String
"Dogs are wonderful but i prefer cats, cats, i can not lie although dog is a true friend".
The first thing I'd do is turn your JSON data into a map which can easily be searched - key would be the word, and value the grade:
var json = [
{"word":"true","grade":1},
{"word":"hate","grade":-1},
{"word":"dog","grade":0.8},
{"word":"cat","grade":-0.8}
];
var map = json.reduce(function(p,c){
p.set(c.word.toLowerCase(),c.grade);
return p;
}, new Map());
console.log(...map);
Then, its just a case of splitting your string, whilst also calculating the total score - again reduce can be used
var json = [
{"word":"true","grade":1},
{"word":"hate","grade":-1},
{"word":"dog","grade":0.8},
{"word":"cat","grade":-0.8}
];
var map = json.reduce(function(p,c){
p.set(c.word.toLowerCase(),c.grade);
return p;
}, new Map());
var input = "Dogs are wonderful but i prefer cats cats i can not lie although dog is a true friend";
var score = input.split(' ').reduce(function(p,c){
var wordScore = map.get(c.toLowerCase()) || 0;
return p + wordScore;
},0);
console.log(score);
Note that I have manually removed punctuation in the above input - I'll leave that as an exercise for you.
Also note that "cats" != "cat" so some of your words wont be found!
Let's first think of the algorithm. Two options:
Search and count the input string as many times as number of words in your JSON, or
Check each word in your input string against the JSON contents.
Since the JSON length is known and (I presume) shorter than the possible input string, I would tend to prefer option 2.
Now, after selecting option 2, you need to split the input string into words and create an array containing one word each entry of the array.
You can achieve this using the mystring.split(" ") method. This, of course, does not take into account punctuations, but you can handle this using the same method.
Now, you can add to each entry in your JSON a field to count the number of appearances of each entry in the JSON within the string.
Finally, you sum the product of the counters and the grade.
console.log((function(rules, str) {
var sum = 0;
Array.prototype.forEach.call(rules, function(rule) {
var match = str.match(rule.regexp);
match && (sum += str.match(rule.regexp).length * rule.grade);
console.log([rule.regexp, match&&match.length, rule.grade, match&&match.length * rule.grade, sum]);
});
return sum;
})([{
"regexp": /true/g,
"grade": 1
}, {
"regexp": /hate/g,
"grade": -1
}, {
"regexp": /dog/g,
"grade": 0.8
}, {
"regexp": /cat/g,
"grade": -0.8
}], "Dogs are wonderful but i prefer cats, cats, i can not lie although dog is a true friend"));
i use regexp rather than string, u can use string and convert to regex at run time, hope this would help
I have a column in my table that shows variations of the following text, where the dates vary
Requested Statement 7/1/2014 - 9/16/2014
tablesorter has trouble sorting this properly, as you can see in this fiddle. The first column will sort when clicked, but the second will not. I also included a table of some string comparisons to show that javascript properly recognizes the order they should be in.
http://jsfiddle.net/kfu4ragh/1/
I tried adding a custom textExtraction function but I'm still getting the same results.
It seems that tablesorter is doing something different than a simple > or < to determine the order of the string values. Is there a way I can alter tablesorter to sort this column correctly?
The problem is that the second column ("Requested Statement...") is being detected as a date column and the parser is trying to turn that entire string into a date; which is invalid.
Here is a demo with the relevant extracted out functions from tablesorter. The result is:
// start with "Requested Statement 7/1/2014 - 9/16/2014"
"Requested Statement 2014/7/1 / 9/16/2014" => 0
So you'll need to use the textExtraction function to target the date (demo):
$('table').tablesorter({
textExtraction : function(node){
var txt = $(node).text();
if (/request/i.test(txt)) {
// return the 3rd block (first date in range)
txt = txt.split(' ')[2];
}
return txt;
}
});
Note that the second date in the string is completely ignored. If you would like to make the second date matter, try this code (demo):
$('table').tablesorter({
textExtraction : function(node){
var d1, d2,
txt = $(node).text();
if (/request/i.test(txt)) {
// return the 3rd block (first date in range)
txt = txt.split(' ');
d1 = $.tablesorter.formatFloat(new Date(txt[2]).getTime());
d2 = $.tablesorter.formatFloat(new Date(txt[4]).getTime());
// add the times together - there is likely a better
// method but this works in this situation
txt = d1 + d2;
}
return txt;
}
});
I am using the table sorter plugin to sort my tables.
I want to be able to catch date column in format:
dd/MM/yyyy HH:mm
and then sort them correctly (for this I have to switch days with years).
Here is what I've so far:
ts.addParser({
id: "hebreLongDate",
is: function (s) {
return /\d{1,2}[\/\-]\d{1,2}[\/\-]\d{2,4} d{1,2}:d{1,2}/.test(s);
}, format: function (s, table) {
var c = table.config;
s = s.replace(/\-/g, "/");
// reformat the string in ISO format
s = s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/, "$3/$2/$1");
return $.tablesorter.formatFloat(new Date(s).getTime());
}, type: "numeric"
});
It does not work.
I would appreciate any help, especially if it comes with an explantation on the meaning of the correct regex.
Thanks,
Omer
The parser doesn't really validate the date. The is function only detects if the format matches the pattern for the format function which is why it is just easier to make it return false and manually set the parser for a column using the headers option:
headers: {
1: { sorter: "hebreLongDate" }
},
The is function above is requiring a HH:mm within the pattern, so if the first table cell in the column doesn't match, it ignores that parser. So either way it would be better to manually set the parser.
Anyway, here is how I would write the parser you are describing (demo):
$.tablesorter.addParser({
id: "hebreLongDate",
is: function(s) {
return false;
},
format: function(s, table, cell, cellIndex) {
s = s
// replace separators
.replace(/\s+/g," ").replace(/[\-.,]/g, "/")
// reformat dd/mm/yyyy to yyyy/mm/dd
.replace(/(\d{1,2})[\/\s](\d{1,2})[\/\s](\d{4})/, "$3/$2/$1");
return s ? $.tablesorter.formatFloat( (new Date(s).getTime() || ''), table) : s;
},
type: "numeric"
});
As for explaining the regex, there isn't that much of a difference between the code above and what you have in your question. The biggest difference is that the above code ensures that only one space exists between the date and time and that the date can be separated by a slash, dash, period, comma or space (i.e. 1-1-2000, 1 1 2000 etc).
Update: if you want to have this parser be autodetected, then use the following is regex (updated demo). But it is important to note, that this regex cannot distinguish mmddyyyy from ddmmyyyy so it will always detect ddmmyyyy. To override this, set the header sorter option to "shortDate":
is: function(s) {
// testing for ##-##-####, so it's not perfect; time is optional
return (/(^\d{1,2}[\/\s]\d{1,2}[\/\s]\d{4})/).test((s || '').replace(/\s+/g," ").replace(/[\-.,]/g, "/"));
},
This one may be a bit specialized, but here goes anyway:
Page for reference: http://greg-j.com/icvm/anticartel/search-results.html
Plugin for reference: http://tablesorter.com/
If you look at the last two columns for "Total Fines", you'll see the currency output includes $x.xxx billions and $x.xxx millions. The built-in currency parser does not account for this format. The plugin, fortunately, allows for you to write your own parser. However, I'm not getting anywhere.
See if this works, I haven't tested it:
$.tablesorter.addParser({
id: 'monetary',
'is': function(s) {
return false;
},
format: function(s) {
var i = s.split('$').join('');
var suffixes = [
{name:'thousand', mult: 1000},
{name:'million', mult: 1000000},
{name:'billion', mult: 1000000000},
{name:'trillion', mult: 1000000000000}
];
for (var j in suffixes) {
if (i.indexOf(' '+suffixes[j].name) != -1) {
i = i.split(' '+suffixes[j].name).join('');
val = parseFloat(i) * suffixes[j].mult;
}
}
return val;
},
type: 'numeric'
});
$("#cartels").tablesorter({
widgets: ['zebra'],
sortList: [[0,0]],
headers: {
4: {
sorter:'monetary'
},
5: {
sorter:'monetary'
}
}
});
Can you post the code you've tried?
It looks like what they are doing in the example you posted is assigning each word with a number representation, and then sorting by that:
return s.toLowerCase().replace(/good/,2).replace(/medium/,1).replace(/bad/,0);
So in your case one way might be to replace million with the correct number of zeros and the same for billion. So essentially $1 million gets evaluated to $1,000,000 as far as the parser is concerned.
return s.toLowerCase().replace(/million/,000000).replace(/billion/,000000000);
So s is evaluating to $1000000 once the replace function is evaluated.
Just a thought. Not sure if it works, but it might get you on the right track.