Replacing dynamic variable in string UNITY - regex

I am making a simple dialogue system, and would like to "dynamise" some of the sentences.
For exemple, I have a Sentence
Hey Adventurer {{PlayerName}} !
Welcome in the world !
Now In code I am trying to replace that by the real value of the string in my game. I am doing something like this. But it doesn't work. I do have a string PlayerName in my component where the function is situated
Regex regex = new Regex("(?<={{)(.*?)(?=}})");
MatchCollection matches = regex.Matches(sentence);
for(int i = 0; i < matches.Count; i++)
{
Debug.Log(matches[i]);
sentence.Replace("{{"+matches[i]+"}}", this.GetType().GetField(matches[i].ToString()).GetValue(this) as string);
}
return sentence;
But this return me an error, even tho the match is correct.
Any idea of a way to do fix, or do it better?

Here's how I would solve this.
Create a dictionary with keys as the values you wish to replace and values as what you will be replacing them to.
Dictionary<string, string> valuesToReplace;
valuesToReplace = new Dictionary<string, string>();
valuesToReplace.Add("[playerName]", "Max");
valuesToReplace.Add("[day]", "Thursday");
Then check the text for the values in your dictionary.
If you make sure all of your keys start with "[" and end with "]" this will be quick and easy.
List<string> replacements = new List<string>();
//We will save all of the replacements we are about to perform here.
//This is done so we won't be modifying the original string while working on it, which will create problems.
//We will save them in the following format: originalText}newText
for(int i = 0; i < text.Length; i++) //Let's loop through the entire text
{
int startOfVar = 9999;
if(text[i] == '[') //We have found the beginning of a variable
{
startOfVar = i;
}
if(text[i] == ']') //We have found the ending of a variable
{
string replacement = text.Substring(startOfVar, i - startOfVar); //We have found the section we wish to replace
if (valuesToReplace.ContainsKey(replacement))
replacements.Add(replacement + "}" + valuesToReplace[replacement]); //Add the replacement we are about to perform to our dictionary
}
}
//Now let's perform the replacements:
foreach(string replacement in replacements)
{
text = text.Replace(replacement.Split('}')[0], replacement.Split('}')[1]); //We split our line. Remember the old value was on the left of the } and the new value was on the right
}
This will also work much faster, since it allows you to add as many variables as you wish without making the code slower.

Using Regex.Replace method, and a MatchEvaluator delegate (untested):
Dictionary<string, string> Replacements = new Dictionary<string, string>();
Regex DialogVariableRegex = new Regex("(?<={{)(.*?)(?=}})");
string Replace(string sentence) {
DialogVariableRegex.Replace(sentence, EvaluateMatch);
return sentence;
}
string EvaluateMatch(Match match) {
var matchedKey = match.Value;
if (Replacements.ContainsKey(matchedKey))
return Replacements[matchedKey];
else
return ">>MISSING KEY<<";
}

This is kind of old now, but I figured I'd update the accepted code above. It won't work since the start index is reset every time the loop iterates, so setting startOfVar = i gets completely reset by the time it hits the closing character. Plus there are problems if there's an open bracket '[' and no closing one. You can also no longer use those brackets in your text.
There's also setting the splitter to a single character. It tests fine, but if I set my player name to "Rob}ert", that will cause problems when it performs the replacements.
Here is my updated take on the code which I've tested works in Unity:
public string EvaluateVariables(string str)
{
Dictionary<string, string> varDict = GetVariableDictionary();
List<string> varReplacements = new List<string>();
string matchGuid = Guid.NewGuid().ToString();
bool matched = false;
int start = int.MaxValue;
for (int i = 0; i < str.Length; i++)
{
if (str[i] == '{')
{
if (str[i + 1] == '$')
{
start = i;
matched = true;
}
}
else if (str[i] == '}' && matched)
{
string replacement = str.Substring(start, (i - start) + 1);
if (varDict.ContainsKey(replacement))
{
varReplacements.Add(replacement + matchGuid + varDict[replacement]);
}
start = int.MaxValue;
matched = false;
}
}
foreach (string replacement in varReplacements)
{
str = str.Replace(replacement.Split(new string[] { matchGuid }, StringSplitOptions.None)[0], replacement.Split(new string[] { matchGuid }, StringSplitOptions.None)[1]);
}
return str;
}
private Dictionary<string, string> GetVariableDictionary()
{
Dictionary<string, string> varDict = new Dictionary<string, string>();
varDict.Add("{$playerName}", playerName);
varDict.Add("{$npcName}", npcName);
return varDict;
}

Related

Hive regex for extracting keywords from url

Filenames are following :
file:///storage/emulated/0/SHAREit/videos/Dangerous_Hero_(2017)____Latest_South_Indian_Full_Hindi_Dubbed_Movie___2017_.mp4
file:///storage/emulated/0/VidMate/download/%E0%A0_-_Promo_Songs_-_Khiladi_-_Khesari_Lal_-_Bho.mp4
file:///storage/emulated/0/WhatsApp/Media/WhatsApp%20Video/VID-20171222-WA0015.mp4
file:///storage/emulated/0/bluetooth/%5DChitaChola%7B%7D%D8%B9%D8%A7%D9%85%D8%B1%24%20.3gp
I want to write hive regex to extract words from each string.
for example in 1st string output should be : storage,emulated,....
UPDATE
This Code gives me result , but i wanted regex instead of below code.
package uri_keyword_extractor;
import org.apache.hadoop.hive.ql.exec.UDF;
import org.apache.hadoop.io.Text;
import java.util.ArrayList;
public class UDFUrlKeywordExtractor extends UDF {
private Text result = new Text();
public Text evaluate(Text url) {
if (url == null) {
return null;
}
String keywords = url_keyword_maker(url.toString());
result.set(keywords);
return result;
}
private static String url_keyword_maker(String url) {
// TODO Auto-generated method stub
ArrayList<String> keywordAr = new ArrayList<String>();
char[] charAr = url.toCharArray();
for (int i = 0; i < charAr.length; i++) {
int current_index = i;
// check if character is a-z or A-Z
char ch = charAr[i];
StringBuilder sb = new StringBuilder();
while (current_index < charAr.length-1 && isChar(ch)) {
sb.append(ch);
current_index = current_index+1;
ch = charAr[current_index];
}
String word = sb.toString();
if (word.length() >= 2) {
keywordAr.add(word);
}
i = current_index;
}
//
StringBuilder sb = new StringBuilder();
for(int i =0; i < keywordAr.size();i++) {
String current = keywordAr.get(i);
sb.append(current);
if(i < keywordAr.size() -1) {
sb.append(",");
}
}
return sb.toString();
}
private static boolean isChar(char ch) {
// TODO Auto-generated method stub
int ascii_value = (int) ch;
// A-Z => (65,90) a-z => (97,122)
// condition 1 : A-Z , condition 2 : a-z character check
if ( (ascii_value >= 65 && ascii_value <= 90) || (ascii_value >= 97 && ascii_value <= 122) ) {
return true;
} else {
return false;
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
String test1 = "file:///storage/emulated/0/SHAREit/videos/Dangerous_Hero_(2017)____Latest_South_Indian_Full_Hindi_Dubbed_Movie___2017_.mp4";
String test2 = "file:///storage/emulated/0/VidMate/download/%E0%A0_-_Promo_Songs_-_Khiladi_-_Khesari_Lal_-_Bho.mp4";
String test3 = "file:///storage/emulated/0/bluetooth/%5DChitaChola%7B%7D%D8%B9%D8%A7%D9%85%D8%B1%24%20.3gp";
System.out.println(url_keyword_maker(test1).toString());
System.out.println(url_keyword_maker(test2).toString());
System.out.println(url_keyword_maker(test3).toString());
}
}
Use split(str, regex_pattern) function, it splits str using regex as delimiter pattern and returns array. Then use lateral view + epxlode to explode array and filter keywords by length as in your Java code. Then apply collect_set to re-assemble array of keywords+concat_ws(delimeter, str) function to convert array to the delimited string if necessary.
The regex I passed to the split function is '[^a-zA-Z]'.
Demo:
select url_nbr, concat_ws(',',collect_set(key_word)) keywords from
(--your URLs example, url_nbr here is just for reference
select 'file:///storage/emulated/0/SHAREit/videos/Dangerous_Hero_(2017)____Latest_South_Indian_Full_Hindi_Dubbed_Movie___2017_.mp4' as url, 1 as url_nbr union all
select 'file:///storage/emulated/0/VidMate/download/%E0%A0_-_Promo_Songs_-_Khiladi_-_Khesari_Lal_-_Bho.mp4' as url, 2 as url_nbr union all
select 'file:///storage/emulated/0/WhatsApp/Media/WhatsApp%20Video/VID-20171222-WA0015.mp4' as url, 3 as url_nbr union all
select 'file:///storage/emulated/0/bluetooth/%5DChitaChola%7B%7D%D8%B9%D8%A7%D9%85%D8%B1%24%20.3gp' as url, 4 as url_nbr)s
lateral view explode(split(url, '[^a-zA-Z]')) v as key_word
where length(key_word)>=2 --filter here
group by url_nbr
;
Output:
OK
1 file,storage,emulated,SHAREit,videos,Dangerous,Hero,Latest,South,Indian,Full,Hindi,Dubbed,Movie,mp
2 file,storage,emulated,VidMate,download,Promo,Songs,Khiladi,Khesari,Lal,Bho,mp
3 file,storage,emulated,WhatsApp,Media,Video,VID,WA,mp
4 file,storage,emulated,bluetooth,DChitaChola,gp
Time taken: 37.767 seconds, Fetched: 4 row(s)
Maybe I have missed something from your java code, but hope you have caught the idea, so you can easily modify my code and add additional processing if necessary.

Parse strings and objects from value of cell in spreadsheet

I got spreadsheet including annoying values and struggle to this. Required data is in each cell of column A. Value of a cell is {"p0":70,"u3":71,"s7110":40},t45,{"t78":60,"s3310":15},p37,p36,{"p29":44,"s8110":95},p85,p14,{"s2710":41},u47. Number of such values is about 1000. I have to parse these values. I need strings and objects from such values. I can ignore order of parsed value. I cannot parse manually. So I decided to use script.
I tried to parse using "split".
var sheet = SpreadsheetApp.getActiveSheet();
var values = sheet.getRange("A1:A" + sheet.getLastRow()).getValues();
var result = [];
for (var i = 0; i < values.length; i++)
{
result.push(values[i][0].split(","));
}
I got t45,p37,p36,p85,p14,u47 as string. But "split" also splits all objects. For example, {"p0":70,"u3":71,"s7110":40} is split to {"p0":70, "u3":71, "s7110":40}. Is there way to solve this?
Sample values are this. Each line is in cell A1, A2, A3, A4, A5.
{"p0":70,"u3":71,"s7110":40},t45,{"t78":60,"s3310":15},p37,p36,{"p29":44,"s8110":95},p85,p14,{"s2710":41},u47
s6610,{"t25":70,"u8":43,"p35":86},u85,u74,{"s7710":83},{"p70":70,"u67":84},{"u71":43,"s1210":73},{"u45":84,"s710":15},{"u14":79,"p22":45},p31
u73,u12,{"t51":98,"u57":96},u31,p41,s1110,s6610,p55,{"t57":71,"s7510":83,"u62":17},u73
t50,{"t83":22,"p18":76},{"p47":12,"s8710":18,"u11":35},{"t14":74,"u72":51},{"p74":21,"t77":77},{"u62":84,"s3010":11},p81,u36,p67,{"t79":12,"u2":70,"s6010":98}
{"u54":51,"t31":31},t56,s4110,{"s3110":84,"t25":92,"p80":19},s3210,{"p65":54,"s8510":45},{"t73":78,"s6210":11},{"s2110":98,"p11":16},{"p61":55,"t88":75},p38
Thank you so much for your time. And I'm sorry for my immature question.
B1:
=ARRAYFORMULA(REGEXEXTRACT(A1:A5&",",REGEXREPLACE(REGEXREPLACE(A1:A5&",","{.*?}","($0)"),"([A-Za-z]\d+),","($1),")))
We are enclosing all objects and strings except the commas , with () and then extracting them later.
EDIT:An easier anchor: The , commas to split by are NOT followed by "
=ARRAYFORMULA(SUBSTITUTE(SPLIT(SUBSTITUTE(A1:A5,","&CHAR(34),"😎"),","),"😎",","&CHAR(34)))
=ARRAYFORMULA(split(REGEXREPLACE(A1:A5,"(,)([^"&CHAR(34)&"])","😋$2"),"😋"))
You can use RegExp to replace characters with something unique that can be found without affecting anything else.
function myFunction() {
var L,newArray,thisElement;
var myStrng = '{"p0":70,"u3":71,"s7110":40},t45,{"t78":60,"s3310":15},p37,p36,{"p29":44,"s8110":95},p85,p14,{"s2710":41},u47 \
s6610,{"t25":70,"u8":43,"p35":86},u85,u74,{"s7710":83},{"p70":70,"u67":84},{"u71":43,"s1210":73},{"u45":84,"s710":15},{"u14":79,"p22":45},p31 \
u73,u12,{"t51":98,"u57":96},u31,p41,s1110,s6610,p55,{"t57":71,"s7510":83,"u62":17},u73 \
t50,{"t83":22,"p18":76},{"p47":12,"s8710":18,"u11":35},{"t14":74,"u72":51},{"p74":21,"t77":77},{"u62":84,"s3010":11},p81,u36,p67,{"t79":12,"u2":70,"s6010":98} \
{"u54":51,"t31":31},t56,s4110,{"s3110":84,"t25":92,"p80":19},s3210,{"p65":54,"s8510":45},{"t73":78,"s6210":11},{"s2110":98,"p11":16},{"p61":55,"t88":75},p38';
var re = new RegExp("\},","g");
var parsedObj = myStrng.replace(re,"}zq^");//Replace all }, characters with }zq^
//Logger.log(parsedObj)
parsedObj = parsedObj.replace(/,\{/g,"zq^{");//Replace all ,{ characters with zq^{
//Logger.log(parsedObj)
parsedObj = parsedObj.replace(/\}\{/g,"}zq^{");//Replace all back to back brackets
parsedObj = parsedObj.replace(/\} \{/g,"}zq^{");//Replace all back to back brackets with a space between
parsedObj = parsedObj.split("zq^");//split on zq^
L = parsedObj.length;
newArray = [];
for (var i=0;i<L;i++) {
thisElement = parsedObj[i];
//Logger.log('thisElement: ' + thisElement)
if (thisElement.indexOf("{") !== -1) {
newArray.push(thisElement);
continue;
}
if (thisElement.indexOf(",") !== -1) {
thisElement = thisElement.split(",");
for (var j =0;j<thisElement.length;j++) {
newArray.push(thisElement[j]);
}
continue;
}
if (thisElement.indexOf(" ") !== -1) {
thisElement = thisElement.split(" ");
for (var j =0;j<thisElement.length;j++) {
newArray.push(thisElement[j]);
}
continue;
}
newArray.push(thisElement);
}
L = newArray.length;
for (var i=0;i<L;i++) {
Logger.log(newArray[i])
}
}

RE2 regular expressions on streams?

Is it possible to use Google RE2 with streams? Some input literals that we are suppose to process with regular expressions can potentially be too large to hold in-memory.
If there is a maximum match length, you could read the data in blocks of at least twice that length. If the match fails, or starts less than that many characters from the end, cut the current string, and append another block.
The length of the match string would never be more than the block length + max match length.
Example in C#:
public static IEnumerable<StreamMatch> MatchesInStream(
this Regex pattern, TextReader reader,
int maxMatchLength, int blockLength)
{
if (maxMatchLength <= 0)
{
throw new ArgumentException("Must be positive", "maxMatchLength");
}
if (blockLength < maxMatchLength)
{
throw new ArgumentException("Must be at least as long as maxMatchLength", "blockLength");
}
char[] buffer = new char[blockLength];
string chunk = "";
int matchOffset = 0;
// Read one block, and append to the string
int charsRead = reader.ReadBlock(buffer, 0, blockLength);
chunk += new string(buffer, 0, charsRead);
while (charsRead > 0 && chunk.Length > maxMatchLength)
{
int cutPosition = 0;
foreach (Match match in pattern.Matches(chunk))
{
if (match.Index > chunk.Length - maxMatchLength)
{
// The match could possibly have matched more characters.
// Read another block before trying again.
break;
}
yield return new StreamMatch(matchOffset, match);
cutPosition = match.Index + match.Length;
}
cutPosition = Math.Max(cutPosition, chunk.Length - maxMatchLength);
matchOffset += cutPosition;
chunk = chunk.Substring(cutPosition);
charsRead = reader.ReadBlock(buffer, 0, blockLength);
chunk += new string(buffer, 0, charsRead);
}
// Stream has ended. Try to match the last remaining characters.
foreach (Match match in pattern.Matches(chunk))
{
yield return new StreamMatch(matchOffset, match);
}
}
public class StreamMatch
{
public int MatchOffset { get; private set; }
public Match Match { get; private set; }
public StreamMatch(int matchOffset, Match match)
{
MatchOffset = matchOffset;
Match = match;
}
}
// One horrible XML parser
var reader = new StreamReader(stream);
var pattern = new Regex(#"<(/?)([\w:-]{1,15})([^<>]{0,50}(?<!/))(/?)>");
foreach (StreamMatch match in pattern.MatchesInStream(reader, 69, 128))
{
Console.WriteLine(match.Match.Value);
}

Generate all matches from a subset of regex

I need to define a bunch of vector sequences, which are all a series of L,D,R,U for left, down, right, up or x for break. There are optional parts, and either/or parts. I have been using my own invented system for noting it down, but I want to document this for other, potentially non-programmers to read.
I now want to use a subset (I don't plan on using any wildcards, or infinite repetition for example) of regex to define the vector sequence and a script to produce all possible matching strings...
/LDR/ produces ['LDR']
/LDU?R/ produces ['LDR','LDUR']
/R(LD|DR)U/ produces ['RLDU','RDRU']
/DxR[DL]U?RDRU?/ produces ['DxRDRDR','DxRDRDRU','DxRDURDR','DxRDURDRU','DxRLRDR','DxRLRDRU','DxRLURDR','DxRLURDRU']
Is there an existing library I can use to generate all matches?
EDIT
I realised I will only be needing or statements, as optional things can be specified by thing or nothing maybe a, or b, both optional could be (a|b|). Is there another language I could use to define what I am trying to do?
By translating the java code form the link provided by #Dukeling into javascript, I think I have solved my problem...
var Node = function(str){
this.bracket = false;
this.children = [];
this.s = str;
this.next = null;
this.addChild = function(child){
this.children.push(child);
}
}
var printTree = function(root,prefix){
prefix = prefix.replace(/\./g, "");
for(i in root.children){
var child = root.children[i]
printTree(child, prefix + root.s);
}
if(root.children.length < 1){
console.log(prefix + root.s);
}
}
var Stack = function(){
this.arr = []
this.push = function(item){
this.arr.push(item)
}
this.pop = function(){
return this.arr.pop()
}
this.peek = function(){
return this.arr[this.arr.length-1]
}
}
var createTree = function(s){
// this line was causing errors for `a(((b|c)d)e)f` because the `(((` was only
// replacing the forst two brackets.
// var s = s.replace(/(\(|\||\))(\(|\||\))/g, "$1.$2");
// this line fixes it
var s = s.replace(/[(|)]+/g, function(x){ return x.split('').join('.') });
var str = s.split('');
var stack = new Stack();
var root = new Node("");
stack.push(root); // start node
var justFinishedBrackets = false;
for(i in str){
var c = str[i]
if(c == '('){
stack.peek().next = new Node("Y"); // node after brackets
stack.peek().bracket = true; // node before brackets
} else if (c == '|' || c == ')'){
var last = stack.peek(); // for (ab|cd)e, remember b / d so we can add child e to it
while (!stack.peek().bracket){ // while not node before brackets
stack.pop();
}
last.addChild(stack.peek().next); // for (b|c)d, add d as child to b / c
} else {
if (justFinishedBrackets){
var next = stack.pop().next;
next.s = "" + c;
stack.push(next);
} else {
var n = new Node(""+c);
stack.peek().addChild(n);
stack.push(n);
}
}
justFinishedBrackets = (c == ')');
}
return root;
}
// Test it out
var str = "a(c|mo(r|l))e";
var root = createTree(str);
printTree(root, "");
// Prints: ace / amore / amole
I only changed one line, to allow more than two consecutive brackets to be handled, and left the original translation in the comments
I also added a function to return an array of results, instead of printing them...
var getTree = function(root,prefix){
this.out = this.out || []
prefix = prefix.replace(/\./g, "");
for(i in root.children){
var child = root.children[i]
getTree(child, prefix + root.s, out);
}
if(root.children.length < 1){
this.out.push(prefix + root.s);
}
if(!prefix && !root.s){
var out = this.out;
this.out = null
return out;
}
}
// Test it
var str = "a(b|c)d";
var root = createTree(str);
console.log(getTree(root, ""));
// logs ["abd","acd"]
The last part, to allow for empty strings too, so... (ab|c|) means ab or c or nothing, and a convenience shortcut so that ab?c is translated into a(b|)c.
var getMatches = function(str){
str = str.replace(/(.)\?/g,"($1|)")
// replace all instances of `(???|)` with `(???|µ)`
// the µ will be stripped out later
str = str.replace(/\|\)/g,"|µ)")
// fix issues where last character is `)` by inserting token `µ`
// which will be stripped out later
str = str+"µ"
var root = createTree(str);
var res = getTree(root, "");
// strip out token µ
for(i in res){
res[i] = res[i].replace(/µ/g,"")
}
// return the array of results
return res
}
getMatches("a(bc|de?)?f");
// Returns: ["abcf","adef","adf","af"]
The last part is a little hack-ish as it relies on µ not being in the string (not an issue for me) and solves one bug, where a ) at the end on the input string was causing incorrect output, by inserting a µ at the end of each string, and then stripping it from the results. I would be happy for someone to suggest a better way to handle these issues, so it can work as a more general solution.
This code as it stands does everything I need. Thanks for all your help!
I'd imagine what you're trying is quite easy with a tree (as long as it's only or-statements).
Parse a(b|c)d (or any or-statement) into a tree as follows: a has children b and c, b and c have a mutual child d. b and c can both consist of 0 or more nodes (as in c could be g(e|f)h in which case (part of) the tree would be a -> g -> e/f (2 nodes) -> h -> d or c could be empty, in which case (part of) the tree would be a -> d, but an actual physical empty node may simplify things, which you should see when trying to write the code).
Generation of the tree shouldn't be too difficult with either recursion or a stack.
Once you have a tree, it's trivial to recursively iterate through the whole thing and generate all strings.
Also, here is a link to a similar question, providing a library or two.
EDIT:
"shouldn't be too difficult" - okay, maybe not
Here is a somewhat complicated example (Java) that may require some advanced knowledge about stacks.
Here is a slightly simpler version (Java) thanks to inserting a special character between each ((, )), |(, etc.
Note that neither of these are particularly efficient, the point is just to get the idea across.
Here is a JavaScript example that addresses parsing the (a|b) and (a|b|) possibilities, creates an array of possible substrings, and composes the matches based on this answer.
var regex = /\([RLUD]*\|[RLUD]*\|?\)/,
str = "R(LD|DR)U(R|L|)",
substrings = [], matches = [], str_tmp = str, find
while (find = regex.exec(str_tmp)){
var index = find.index
finds = find[0].split(/\|/)
substrings.push(str_tmp.substr(0, index))
if (find[0].match(/\|/g).length == 1)
substrings.push([finds[0].substr(1), finds[1].replace(/.$/, '')])
else if (find[0].match(/\|/g).length == 2){
substrings.push([finds[0].substr(1), ""])
substrings.push([finds[1], ""])
}
str_tmp = str_tmp.substr(index + find[0].length)
}
if (str_tmp) substrings.push([str_tmp])
console.log(substrings) //>>["R", ["LD", "DR"], "U", ["R", ""], ["L", ""]]
//compose matches
function printBin(tree, soFar, iterations) {
if (iterations == tree.length) matches.push(soFar)
else if (tree[iterations].length == 2){
printBin(tree, soFar + tree[iterations][0], iterations + 1)
printBin(tree, soFar + tree[iterations][1], iterations + 1)
}
else printBin(tree, soFar + tree[iterations], iterations + 1)
}
printBin(substrings, "", 0)
console.log(matches) //>>["RLDURL", "RLDUR", "RLDUL", "RLDU", "RDRURL", "RDRUR", "RDRUL", "RDRU"]

Fuzzy Matches on dijit.form.ComboBox / dijit.form.FilteringSelect Subclass

I am trying to extend dijit.form.FilteringSelect with the requirement that all instances of it should match input regardless of where the characters are in the inputted text, and should also ignore whitespace and punctuation (mainly periods and dashes).
For example if an option is "J.P. Morgan" I would want to be able to select that option after typing "JP" or "P Morgan".
Now I know that the part about matching anywhere in the string can be accomplished by passing in queryExpr: "*${0}*" when creating the instance.
What I haven't figured out is how to make it ignore whitespace, periods, and dashes. I have an example of where I'm at here - http://jsfiddle.net/mNYw2/2/. Any help would be appreciated.
the thing to master in this case is the store fetch querystrings.. It will call a function in the attached store to pull out any matching items, so if you have a value entered in the autofilling inputfield, it will eventually end up similar to this in the code:
var query = { this.searchAttr: this.get("value") }; // this is not entirely accurate
this._fetchHandle = this.store.query(query, options);
this._fetchHandle.then( showResultsFunction );
So, when you define select, override the _setStoreAttr to make changes in the store query api
dojo.declare('CustomFilteringSelect', [FilteringSelect], {
constructor: function() {
//???
},
_setStoreAttr: function(store) {
this.inherited(arguments); // allow for comboboxmixin to modify it
// above line eventually calls this._set("store", store);
// so now, 'this' has 'store' set allready
// override here
this.store.query = function(query, options) {
// note that some (Memory) stores has no 'fetch' wrapper
};
}
});
EDIT: override queryEngine function as opposed to query function
Take a look at the file SimpleQueryEngine.js under dojo/store/util. This is essentially what filters the received Array items on the given String query from the FilteringSelect. Ok, it goes like this:
var MyEngine = function(query, options) {
// create our matching query function
switch(typeof query){
default:
throw new Error("Can not query with a " + typeof query);
case "object": case "undefined":
var queryObject = query;
query = function(object){
for(var key in queryObject){
var required = queryObject[key];
if(required && required.test){
if(!required.test(object[key])){
return false;
}
}else if(required != object[key]){
return false;
}
}
return true;
};
break;
case "string":
/// HERE is most likely where you can play with the reqexp matcher.
// named query
if(!this[query]){
throw new Error("No filter function " + query + " was found in store");
}
query = this[query];
// fall through
case "function":
// fall through
}
function execute(array){
// execute the whole query, first we filter
var results = arrayUtil.filter(array, query);
// next we sort
if(options && options.sort){
results.sort(function(a, b){
for(var sort, i=0; sort = options.sort[i]; i++){
var aValue = a[sort.attribute];
var bValue = b[sort.attribute];
if (aValue != bValue) {
return !!sort.descending == aValue > bValue ? -1 : 1;
}
}
return 0;
});
}
// now we paginate
if(options && (options.start || options.count)){
var total = results.length;
results = results.slice(options.start || 0, (options.start || 0) + (options.count || Infinity));
results.total = total;
}
return results;
}
execute.matches = query;
return execute;
};
new Store( { queryEngine: MyEngine });
when execute.matches is set on bottom of this function, what happens is, that the string gets called on each item. Each item has a property - Select.searchAttr - which is tested by RegExp like so: new RegExp(query).test(item[searchAttr]); or maybe a bit simpler to understand; item[searchAttr].matches(query);
I have no testing environment, but locate the inline comment above and start using console.debug..
Example:
Stpre.data = [
{ id:'WS', name: 'Will F. Smith' },
{ id:'RD', name:'Robert O. Dinero' },
{ id:'CP', name:'Cle O. Patra' }
];
Select.searchAttr = "name";
Select.value = "Robert Din"; // keyup->autocomplete->query
Select.query will become Select.queryExp.replace("${0]", Select.value), in your simple queryExp case, 'Robert Din'.. This will get fuzzy and it would be up to you to fill in the regular expression, here's something to start with
query = query.substr(1,query.length-2); // '*' be gone
var words = query.split(" ");
var exp = "";
dojo.forEach(words, function(word, idx) {
// check if last word
var nextWord = words[idx+1] ? words[idx+1] : null;
// postfix 'match-all-but-first-letter-of-nextWord'
exp += word + (nextWord ? "[^" + nextWord[0] + "]*" : "");
});
// exp should now be "Robert[^D]*Din";
// put back '*'
query = '*' + exp + '*';