Compare commits

...
Author SHA1 Message Date
saqimtiaz 548c4211e5 feat: added tests 2026-07-26 15:38:11 +02:00
saqimtiaz a5e228cd11 docs: added changenote 2026-07-26 14:57:26 +02:00
saqimtiaz eb9d414204 refactor: prefix operator 2026-07-24 18:50:54 +02:00
saqimtiaz 44f585437c refactor: optimize listops.js using ES2017 2026-07-24 18:36:46 +02:00
saqimtiaz 8d41b32ddc chore: eslint 2026-07-24 18:29:18 +02:00
saqimtiaz 574d3e72bd refactor: use ES2017 to optimize x-listops 2026-07-24 18:18:58 +02:00
5 changed files with 145 additions and 167 deletions
+30 -54
View File
@@ -9,69 +9,56 @@ Filter operators for manipulating the current selection list
"use strict"; "use strict";
/*
Fetch titles from the current list
*/
const prepare_results = (source) => {
const results = [];
source((tiddler,title) => results.push(title));
return results;
};
/* /*
Order a list Order a list
*/ */
exports.order = function(source,operator,options) { exports.order = function(source,operator,options) {
var results = []; const results = prepare_results(source);
if(operator.operand.toLowerCase() === "reverse") { return operator.operand.toLowerCase() === "reverse" ?
source(function(tiddler,title) { results.reverse() :
results.unshift(title); results;
});
} else {
source(function(tiddler,title) {
results.push(title);
});
}
return results;
}; };
/* /*
Reverse list Reverse list
*/ */
exports.reverse = function(source,operator,options) { exports.reverse = function(source,operator,options) {
var results = []; return prepare_results(source).reverse();
source(function(tiddler,title) {
results.unshift(title);
});
return results;
}; };
/* /*
First entry/entries in list First entry/entries in list
*/ */
exports.first = function(source,operator,options) { exports.first = function(source,operator,options) {
var count = $tw.utils.getInt(operator.operand,1), const count = $tw.utils.getInt(operator.operand,1);
results = []; return prepare_results(source).slice(0,count);
source(function(tiddler,title) {
results.push(title);
});
return results.slice(0,count);
}; };
/* /*
Last entry/entries in list Last entry/entries in list
*/ */
exports.last = function(source,operator,options) { exports.last = function(source,operator,options) {
var count = $tw.utils.getInt(operator.operand,1), const count = $tw.utils.getInt(operator.operand,1);
results = []; return count === 0 ?
if(count === 0) return results; [] :
source(function(tiddler,title) { prepare_results(source).slice(-count);
results.push(title);
});
return results.slice(-count);
}; };
/* /*
All but the first entry/entries of the list All but the first entry/entries of the list
*/ */
exports.rest = function(source,operator,options) { exports.rest = function(source,operator,options) {
var count = $tw.utils.getInt(operator.operand,1), const count = $tw.utils.getInt(operator.operand,1);
results = []; return prepare_results(source).slice(count);
source(function(tiddler,title) {
results.push(title);
});
return results.slice(count);
}; };
exports.butfirst = exports.rest; exports.butfirst = exports.rest;
exports.bf = exports.rest; exports.bf = exports.rest;
@@ -80,12 +67,9 @@ exports.bf = exports.rest;
All but the last entry/entries of the list All but the last entry/entries of the list
*/ */
exports.butlast = function(source,operator,options) { exports.butlast = function(source,operator,options) {
var count = $tw.utils.getInt(operator.operand,1), const count = $tw.utils.getInt(operator.operand,1),
results = []; results = prepare_results(source),
source(function(tiddler,title) { index = count === 0 ? results.length : -count;
results.push(title);
});
var index = count === 0 ? results.length : -count;
return results.slice(0,index); return results.slice(0,index);
}; };
exports.bl = exports.butlast; exports.bl = exports.butlast;
@@ -94,22 +78,14 @@ exports.bl = exports.butlast;
The nth member of the list The nth member of the list
*/ */
exports.nth = function(source,operator,options) { exports.nth = function(source,operator,options) {
var count = $tw.utils.getInt(operator.operand,1), const count = $tw.utils.getInt(operator.operand,1);
results = []; return prepare_results(source).slice(count - 1,count);
source(function(tiddler,title) {
results.push(title);
});
return results.slice(count - 1,count);
}; };
/* /*
The zero based nth member of the list The zero based nth member of the list
*/ */
exports.zth = function(source,operator,options) { exports.zth = function(source,operator,options) {
var count = $tw.utils.getInt(operator.operand,0), const count = $tw.utils.getInt(operator.operand,0);
results = []; return prepare_results(source).slice(count,count + 1);
source(function(tiddler,title) { };
results.push(title);
});
return results.slice(count,count + 1);
};
+18 -32
View File
@@ -13,37 +13,23 @@ Filter operator for checking if a title starts with a prefix
Export our filter function Export our filter function
*/ */
exports.prefix = function(source,operator,options) { exports.prefix = function(source,operator,options) {
var results = [], const results = [],
suffixes = (operator.suffixes || [])[0] || []; suffixes = (operator.suffixes || [])[0] || [],
if(suffixes.indexOf("caseinsensitive") !== -1) { caseInsensitive = suffixes.indexOf("caseinsensitive") !== -1,
var operand = operator.operand.toLowerCase(); negate = operator.prefix === "!";
if(operator.prefix === "!") {
source(function(tiddler,title) { const operand = caseInsensitive ?
if(title.toLowerCase().substr(0,operand.length) !== operand) { operator.operand.toLowerCase() :
results.push(title); operator.operand;
}
}); source((tiddler,title) => {
} else { const value = caseInsensitive ? title.toLowerCase() : title;
source(function(tiddler,title) { const matches = value.startsWith(operand);
if(title.toLowerCase().substr(0,operand.length) === operand) {
results.push(title); if(negate ? !matches : matches) {
} results.push(title);
});
} }
} else { });
if(operator.prefix === "!") {
source(function(tiddler,title) {
if(title.substr(0,operator.operand.length) !== operator.operand) {
results.push(title);
}
});
} else {
source(function(tiddler,title) {
if(title.substr(0,operator.operand.length) === operator.operand) {
results.push(title);
}
});
}
}
return results; return results;
}; };
+70 -81
View File
@@ -11,20 +11,18 @@ Extended filter operators to manipulate the current list.
/* /*
Fetch titles from the current list Fetch titles from the current list
*/ */
var prepare_results = function (source) { const prepare_results = (source) => {
var results = []; const results = [];
source(function (tiddler, title) { source((tiddler,title) => results.push(title));
results.push(title);
});
return results; return results;
}; };
/* /*
Moves a number of items from the tail of the current list before the item named in the operand Moves a number of items from the tail of the current list before the item named in the operand
*/ */
exports.putbefore = function (source, operator) { exports.putbefore = function(source,operator) {
var results = prepare_results(source), const results = prepare_results(source),
index = results.indexOf(operator.operand), index = results.indexOf(operator.operand),
count = $tw.utils.getInt(operator.suffix,1); count = $tw.utils.getInt(operator.suffix,1);
return (index === -1) ? return (index === -1) ?
@@ -34,9 +32,9 @@ exports.putbefore = function (source, operator) {
/* /*
Moves a number of items from the tail of the current list after the item named in the operand Moves a number of items from the tail of the current list after the item named in the operand
*/ */
exports.putafter = function (source, operator) { exports.putafter = function(source,operator) {
var results = prepare_results(source), const results = prepare_results(source),
index = results.indexOf(operator.operand), index = results.indexOf(operator.operand),
count = $tw.utils.getInt(operator.suffix,1); count = $tw.utils.getInt(operator.suffix,1);
return (index === -1) ? return (index === -1) ?
@@ -46,9 +44,9 @@ exports.putafter = function (source, operator) {
/* /*
Replaces the item named in the operand with a number of items from the tail of the current list Replaces the item named in the operand with a number of items from the tail of the current list
*/ */
exports.replace = function (source, operator) { exports.replace = function(source,operator) {
var results = prepare_results(source), const results = prepare_results(source),
index = results.indexOf(operator.operand), index = results.indexOf(operator.operand),
count = $tw.utils.getInt(operator.suffix,1); count = $tw.utils.getInt(operator.suffix,1);
return (index === -1) ? return (index === -1) ?
@@ -58,39 +56,39 @@ exports.replace = function (source, operator) {
/* /*
Moves a number of items from the tail of the current list to the head of the list Moves a number of items from the tail of the current list to the head of the list
*/ */
exports.putfirst = function (source, operator) { exports.putfirst = function(source,operator) {
var results = prepare_results(source), const results = prepare_results(source),
count = $tw.utils.getInt(operator.suffix,1); count = $tw.utils.getInt(operator.suffix,1);
return results.slice(-count).concat(results.slice(0, -count)); return [...results.slice(-count), ...results.slice(0, -count)];
}; };
/* /*
Moves a number of items from the head of the current list to the tail of the list Moves a number of items from the head of the current list to the tail of the list
*/ */
exports.putlast = function (source, operator) { exports.putlast = function(source,operator) {
var results = prepare_results(source), const results = prepare_results(source),
count = $tw.utils.getInt(operator.suffix,1); count = $tw.utils.getInt(operator.suffix,1);
return results.slice(count).concat(results.slice(0, count)); return [...results.slice(count), ...results.slice(0, count)];
}; };
/* /*
Moves the item named in the operand a number of places forward or backward in the list Moves the item named in the operand a number of places forward or backward in the list
*/ */
exports.move = function (source, operator) { exports.move = function(source,operator) {
var results = prepare_results(source), const results = prepare_results(source),
index = results.indexOf(operator.operand), index = results.indexOf(operator.operand),
count = $tw.utils.getInt(operator.suffix,1), count = $tw.utils.getInt(operator.suffix,1),
marker = results.splice(index, 1), marker = results.splice(index, 1),
offset = (index + count) > 0 ? index + count : 0; offset = (index + count) > 0 ? index + count : 0;
return results.slice(0, offset).concat(marker).concat(results.slice(offset)); return results.slice(0, offset).concat(marker).concat(results.slice(offset));
}; };
/* /*
Returns the items from the current list that are after the item named in the operand Returns the items from the current list that are after the item named in the operand
*/ */
exports.allafter = function (source, operator) { exports.allafter = function(source,operator) {
var results = prepare_results(source), const results = prepare_results(source),
index = results.indexOf(operator.operand); index = results.indexOf(operator.operand);
return (index === -1) ? [] : return (index === -1) ? [] :
(operator.suffix) ? results.slice(index) : (operator.suffix) ? results.slice(index) :
@@ -99,9 +97,9 @@ exports.allafter = function (source, operator) {
/* /*
Returns the items from the current list that are before the item named in the operand Returns the items from the current list that are before the item named in the operand
*/ */
exports.allbefore = function (source, operator) { exports.allbefore = function(source,operator) {
var results = prepare_results(source), const results = prepare_results(source),
index = results.indexOf(operator.operand); index = results.indexOf(operator.operand);
return (index === -1) ? [] : return (index === -1) ? [] :
(operator.suffix) ? results.slice(0, index + 1) : (operator.suffix) ? results.slice(0, index + 1) :
@@ -110,9 +108,9 @@ exports.allbefore = function (source, operator) {
/* /*
Appends the items listed in the operand array to the tail of the current list Appends the items listed in the operand array to the tail of the current list
*/ */
exports.append = function (source, operator) { exports.append = function(source,operator) {
var append = $tw.utils.parseStringArray(operator.operand, "true"), const append = $tw.utils.parseStringArray(operator.operand,"true"),
results = prepare_results(source), results = prepare_results(source),
count = parseInt(operator.suffix) || append.length; count = parseInt(operator.suffix) || append.length;
return (append.length === 0) ? results : return (append.length === 0) ? results :
@@ -122,9 +120,9 @@ exports.append = function (source, operator) {
/* /*
Prepends the items listed in the operand array to the head of the current list Prepends the items listed in the operand array to the head of the current list
*/ */
exports.prepend = function (source, operator) { exports.prepend = function(source,operator) {
var prepend = $tw.utils.parseStringArray(operator.operand, "true"), const prepend = $tw.utils.parseStringArray(operator.operand,"true"),
results = prepare_results(source), results = prepare_results(source),
count = $tw.utils.getInt(operator.suffix,prepend.length); count = $tw.utils.getInt(operator.suffix,prepend.length);
return (prepend.length === 0) ? results : return (prepend.length === 0) ? results :
@@ -134,21 +132,19 @@ exports.prepend = function (source, operator) {
/* /*
Returns all items from the current list except the items listed in the operand array Returns all items from the current list except the items listed in the operand array
*/ */
exports.remove = function (source, operator) { exports.remove = function(source, operator) {
var array = $tw.utils.parseStringArray(operator.operand, "true"), const array = $tw.utils.parseStringArray(operator.operand, "true"),
results = prepare_results(source), results = prepare_results(source);
count = parseInt(operator.suffix) || array.length,
p, if(array.length === 0) {
len, return results;
index; }
len = array.length - 1; const count = parseInt(operator.suffix, 10) || array.length,
for(p = 0; p < count; ++p) { targetItems = operator.prefix ? array.slice(-count).reverse() : array.slice(0, count);
if(operator.prefix) {
index = results.indexOf(array[len - p]); for(const item of targetItems) {
} else { const index = results.indexOf(item);
index = results.indexOf(array[p]);
}
if(index !== -1) { if(index !== -1) {
results.splice(index, 1); results.splice(index, 1);
} }
@@ -158,40 +154,32 @@ exports.remove = function (source, operator) {
/* /*
Returns all items from the current list sorted in the order of the items in the operand array Returns all items from the current list sorted in the order of the items in the operand array
*/ */
exports.sortby = function (source, operator) { exports.sortby = function(source,operator) {
var results = prepare_results(source); const results = prepare_results(source);
if(!results || results.length < 2) { if(!results || results.length < 2) {
return results; return results;
} }
var lookup = $tw.utils.parseStringArray(operator.operand, "true"); const lookup = $tw.utils.parseStringArray(operator.operand,"true");
results.sort(function (a, b) { return results.sort((a,b) => lookup.indexOf(a) - lookup.indexOf(b));
return lookup.indexOf(a) - lookup.indexOf(b);
});
return results;
}; };
/* /*
Removes all duplicate items from the current list Removes all duplicate items from the current list
*/ */
exports.unique = function (source, operator) { exports.unique = function(source, operator) {
var results = prepare_results(source); return Array.from(new Set(prepare_results(source)));
var set = results.reduce(function (a, b) {
if(a.indexOf(b) < 0) {
a.push(b);
}
return a;
}, []);
return set;
}; };
var cycleValueInArray = function(results,operands,stepSize) { const cycleValueInArray = function(results,operands,stepSize) {
var resultsIndex, let resultsIndex,
step = stepSize || 1, step = stepSize || 1,
i = 0, i = 0,
opLength = operands.length,
nextOperandIndex; nextOperandIndex;
for(i; i < opLength; i++) { const opLength = operands.length;
for(; i < opLength; i++) {
resultsIndex = results.indexOf(operands[i]); resultsIndex = results.indexOf(operands[i]);
if(resultsIndex !== -1) { if(resultsIndex !== -1) {
break; break;
@@ -213,18 +201,19 @@ var cycleValueInArray = function(results,operands,stepSize) {
/* /*
Toggles an item in the current list. Toggles an item in the current list.
*/ */
exports.toggle = function(source,operator) { exports.toggle = function(source,operator) {
return cycleValueInArray(prepare_results(source),operator.operands); return cycleValueInArray(prepare_results(source),operator.operands);
}; };
exports.cycle = function(source,operator) { exports.cycle = function(source,operator) {
var results = prepare_results(source), const results = prepare_results(source),
operands = (operator.operand.length ? $tw.utils.parseStringArray(operator.operand, "true") : [""]), operands = operator.operand.length ? $tw.utils.parseStringArray(operator.operand,"true") : [""];
step = $tw.utils.getInt(operator.operands[1]||"",1); let step = $tw.utils.getInt(operator.operands[1] || "",1);
if(step < 0) { if(step < 0) {
operands.reverse(); operands.reverse();
step = Math.abs(step); step = Math.abs(step);
} }
return cycleValueInArray(results,operands,step); return cycleValueInArray(results,operands,step);
}; };
@@ -0,0 +1,14 @@
title: Filters/ListOps
description: Test listops operators
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
(<$text text={{{ =[[E]] =[[A]] =[[B]] =[[C]] =[[C]] =[[D]] =[[C]] +[unique[]join[]] }}}/>)
+
title: ExpectedResult
<p>(EABCD)</p>
@@ -0,0 +1,13 @@
change-category: filters
change-type: enhancement
created: 20260726124618748
description: Optimizes listops filter operators
github-contributors: saqimtiaz
github-links: https://github.com/TiddlyWiki/TiddlyWiki5/issues/9942
modified: 20260726125232992
release: 5.5.0
tags: $:/tags/ChangeNote
title: $:/changenotes/5.5.0/#9942
type: text/vnd.tiddlywiki
* The listops filter operators have been rewritten using more modern JavaScript (ES2017). The `unique` and `remove` operators have improved performance in certain circumstances.