2222 * - `Object`: A pattern object can be used to filter specific properties on objects contained
2323 * by `array`. For example `{name:"M", phone:"1"}` predicate will return an array of items
2424 * which have property `name` containing "M" and property `phone` containing "1". A special
25- * property name `$` can be used (as in `{$:"text"}`) to accept a match against any
26- * property of the object or its nested object properties. That's equivalent to the simple
27- * substring match with a `string` as described above. The predicate can be negated by prefixing
28- * the string with `!`.
25+ * property name (`$` by default) can be used (e.g. as in `{$: "text"}`) to accept a match
26+ * against any property of the object or its nested object properties. That's equivalent to the
27+ * simple substring match with a `string` as described above. The special property name can be
28+ * overwritten, using the `anyPropertyKey` parameter.
29+ * The predicate can be negated by prefixing the string with `!`.
2930 * For example `{name: "!M"}` predicate will return an array of items which have property `name`
3031 * not containing "M".
3132 *
5960 * Primitive values are converted to strings. Objects are not compared against primitives,
6061 * unless they have a custom `toString` method (e.g. `Date` objects).
6162 *
63+ * @param {string= } anyPropertyKey The special property name that matches against any property.
64+ * By default `$`.
65+ *
6266 * @example
6367 <example>
6468 <file name="index.html">
127131 </file>
128132 </example>
129133 */
134+
130135function filterFilter ( ) {
131- return function ( array , expression , comparator ) {
136+ return function ( array , expression , comparator , anyPropertyKey ) {
132137 if ( ! isArrayLike ( array ) ) {
133138 if ( array == null ) {
134139 return array ;
@@ -137,6 +142,7 @@ function filterFilter() {
137142 }
138143 }
139144
145+ anyPropertyKey = anyPropertyKey || '$' ;
140146 var expressionType = getTypeForFilter ( expression ) ;
141147 var predicateFn ;
142148 var matchAgainstAnyProp ;
@@ -153,7 +159,7 @@ function filterFilter() {
153159 //jshint -W086
154160 case 'object' :
155161 //jshint +W086
156- predicateFn = createPredicateFn ( expression , comparator , matchAgainstAnyProp ) ;
162+ predicateFn = createPredicateFn ( expression , comparator , anyPropertyKey , matchAgainstAnyProp ) ;
157163 break ;
158164 default :
159165 return array ;
@@ -164,8 +170,8 @@ function filterFilter() {
164170}
165171
166172// Helper functions for `filterFilter`
167- function createPredicateFn ( expression , comparator , matchAgainstAnyProp ) {
168- var shouldMatchPrimitives = isObject ( expression ) && ( '$' in expression ) ;
173+ function createPredicateFn ( expression , comparator , anyPropertyKey , matchAgainstAnyProp ) {
174+ var shouldMatchPrimitives = isObject ( expression ) && ( anyPropertyKey in expression ) ;
169175 var predicateFn ;
170176
171177 if ( comparator === true ) {
@@ -193,25 +199,25 @@ function createPredicateFn(expression, comparator, matchAgainstAnyProp) {
193199
194200 predicateFn = function ( item ) {
195201 if ( shouldMatchPrimitives && ! isObject ( item ) ) {
196- return deepCompare ( item , expression . $ , comparator , false ) ;
202+ return deepCompare ( item , expression [ anyPropertyKey ] , comparator , anyPropertyKey , false ) ;
197203 }
198- return deepCompare ( item , expression , comparator , matchAgainstAnyProp ) ;
204+ return deepCompare ( item , expression , comparator , anyPropertyKey , matchAgainstAnyProp ) ;
199205 } ;
200206
201207 return predicateFn ;
202208}
203209
204- function deepCompare ( actual , expected , comparator , matchAgainstAnyProp , dontMatchWholeObject ) {
210+ function deepCompare ( actual , expected , comparator , anyPropertyKey , matchAgainstAnyProp , dontMatchWholeObject ) {
205211 var actualType = getTypeForFilter ( actual ) ;
206212 var expectedType = getTypeForFilter ( expected ) ;
207213
208214 if ( ( expectedType === 'string' ) && ( expected . charAt ( 0 ) === '!' ) ) {
209- return ! deepCompare ( actual , expected . substring ( 1 ) , comparator , matchAgainstAnyProp ) ;
215+ return ! deepCompare ( actual , expected . substring ( 1 ) , comparator , anyPropertyKey , matchAgainstAnyProp ) ;
210216 } else if ( isArray ( actual ) ) {
211217 // In case `actual` is an array, consider it a match
212218 // if ANY of it's items matches `expected`
213219 return actual . some ( function ( item ) {
214- return deepCompare ( item , expected , comparator , matchAgainstAnyProp ) ;
220+ return deepCompare ( item , expected , comparator , anyPropertyKey , matchAgainstAnyProp ) ;
215221 } ) ;
216222 }
217223
@@ -220,21 +226,21 @@ function deepCompare(actual, expected, comparator, matchAgainstAnyProp, dontMatc
220226 var key ;
221227 if ( matchAgainstAnyProp ) {
222228 for ( key in actual ) {
223- if ( ( key . charAt ( 0 ) !== '$' ) && deepCompare ( actual [ key ] , expected , comparator , true ) ) {
229+ if ( ( key . charAt ( 0 ) !== '$' ) && deepCompare ( actual [ key ] , expected , comparator , anyPropertyKey , true ) ) {
224230 return true ;
225231 }
226232 }
227- return dontMatchWholeObject ? false : deepCompare ( actual , expected , comparator , false ) ;
233+ return dontMatchWholeObject ? false : deepCompare ( actual , expected , comparator , anyPropertyKey , false ) ;
228234 } else if ( expectedType === 'object' ) {
229235 for ( key in expected ) {
230236 var expectedVal = expected [ key ] ;
231237 if ( isFunction ( expectedVal ) || isUndefined ( expectedVal ) ) {
232238 continue ;
233239 }
234240
235- var matchAnyProperty = key === '$' ;
241+ var matchAnyProperty = key === anyPropertyKey ;
236242 var actualVal = matchAnyProperty ? actual : actual [ key ] ;
237- if ( ! deepCompare ( actualVal , expectedVal , comparator , matchAnyProperty , matchAnyProperty ) ) {
243+ if ( ! deepCompare ( actualVal , expectedVal , comparator , anyPropertyKey , matchAnyProperty , matchAnyProperty ) ) {
238244 return false ;
239245 }
240246 }
0 commit comments