9

Was looking for "equivalent for some method in javascript" and "return just one value if is in array", but saw only the answers to the way in which to determine the type of variables or too many unnecessary.

I bypass all inputs in html and i want something like this:

$('#goodsFilter')
    .find('input[type="number"]')
    .some(function(i,el){
        return (isNumber($(el).val())) ? 1 : 0;
});

But it throws an error:

"TypeError: 'undefined' is not a function" (eg. Safari 6.0.4).


UPD: Error comes from the last line, yeah, where });. isNumber:

function isNumber(n) { return !isNaN(parseFloat(n)) && isFinite(n); }

This should check for the presence of each input information, and, if at least one of them is not empty, return 1, otherwise 0. How can I replace it to work in most modern browsers?

UPD: Problem was solved. I'm a little confused in choosing the answer. The code of @RobG implementation of .some() is more understandable for beginners (and I am) so I switched my vote.

trogwar
  • 216
  • 1
  • 11
  • 26
  • `$(el).val()` is much more efficient as `el.value`. What does the `isNumber` function do? You can likely use `return !isNaN(el.value);` – RobG Apr 29 '13 at 03:07
  • So, what's `.some()`? For the sake of completeness – Alexander Apr 29 '13 at 03:09
  • @Alexander—I'll guess that it's either the built–in [`Array.prototype.some`](http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.17) or a native emulation. – RobG Apr 29 '13 at 03:10
  • @RobG, well, if it's the built-in then it's wrong because he is chaining it after a jQuery method - that's being naive – Alexander Apr 29 '13 at 03:11
  • @RobG, thanks for `el.value` yeah, this function do this: `function isNumber(n) { return !isNaN(parseFloat(n)) && isFinite(n); }` – trogwar Apr 29 '13 at 03:12
  • If you solved your own problem, either flag the post for deletion or post your answer and accept it. Don't modify the title with "solved" please. – j08691 Apr 29 '13 at 03:41
  • It is worth noting that this code will run through all items even if the first one already fulfilled the condition, so its performance will always be measured by the worst possible case. Also, polluting the outer scope with a variable for this check seems pretty hack-ish. – diego nunes Apr 29 '13 at 03:47
  • @Diego Nunes, yeah, i totally agree with you. But how can i solve this without .some()? And, if I'm not mistaken, there is no way to break .each() or .forEach() loops. – trogwar Apr 29 '13 at 03:51
  • @TrogWar: You can break `.each()` with a `return false;`. So just put it after your `n = 1`. Doesn't work with the standard `.forEach()` though *(which doesn't matter, because if you have `.forEach()`, you should have `.some()`)*. –  Apr 29 '13 at 03:52
  • You can just emulate the "some" function with a common for/while. (like `function eSome(arr, f) { for (var i in arr) { if (f(i, arr[i])) { return true; } return false; }`) If you want to chain the `.some` call in that jQuery object, you can add it as a jQuery function as well (using `jQuery.fn.some = function (f) { ... }`). – diego nunes Apr 29 '13 at 03:56
  • As it grew up with examples I posted it as a little more comprehensive answer. – diego nunes Apr 29 '13 at 04:06
  • Thanks a lot, guys! I think to test both (they working!) and use the one that will run faster. – trogwar Apr 29 '13 at 04:08
  • If you're actually worried about performance, ditch jQuery, do the DOM selection using native methods, use a `for` loop, and do the input type filtering and value checking in the same loop, and of course `break;` when satisfied. –  Apr 29 '13 at 04:12
  • ...and don't use `for-in` on an Array in JavaScript. Use a `for` loop. In JavaScript, `for-in` on Arrays has too much potential for problems, and is better reserved for some very narrow edge cases. –  Apr 29 '13 at 04:14
  • @Alexander—there is no jQuery *some* method, so I suppose that's the issue. – RobG Apr 29 '13 at 05:11
  • Thanks for the information. I'll run some simple tests – just because using jquery is not a bottleneck in my website. And big thanks for `for-in` information – that was very helpful for me! – trogwar Apr 29 '13 at 05:51

7 Answers7

12

For anyone else who comes to this thread, you can use some() on a jQuery object this way:

 $.makeArray($(...)).some(function(x) { ... })

jQuery.makeArray() converts the jQuery object into an Array, so you can use some() on it.


As suggested by @alf-eaton, you could use:

$(…).toArray().some(function(node) { … })
JesusIniesta
  • 10,412
  • 1
  • 35
  • 28
Alex D
  • 29,755
  • 7
  • 80
  • 126
  • 17
    Or even simpler, use [`toArray()`](https://api.jquery.com/toArray/): `$(…).toArray().some(function(node) { … })` – Alf Eaton Apr 24 '14 at 12:58
  • 1
    Providing you've added *Array.prototype.some* to non ES5 hosts. ;-) The accepted answer adds *.some* to jQuery so you just do `$(...).some(...)`. – RobG Nov 03 '14 at 20:30
8

Array.prototype.some returns true or false, so you can do:

.some(function(el){
        return !isNaN(el.value);
}

You don't say where the error comes from, is it from the call to isNumber?

Edit

Ah, so your issue is with some.

If you want a jQuery some method, then it should at least mimic the built–in ECMAScript some, which takes two arguments: a callback function and an optional this argument.

The callback function should take three arguments: the value, the index (optional) and an optional value to use as the this argument. It should access the numeric members in ascending order and only visit members that actually exist.

So it should be something like (noting that jQuery.fn === jQuery.prototype):

jQuery.fn.some = function(fn, thisArg) {
  var result;

  for (var i=0, iLen = this.length; i<iLen; i++) {

    if (this.hasOwnProperty(i)) {

      if (typeof thisArg == 'undefined') {
        result = fn(this[i], i, this);

      } else {
        result = fn.call(thisArg, this[i], i, this);
      }

      if (result) return true;
    }  
  }
  return false;
}

So if you want now you can do:

var result = $('#goodsFilter')
              .find('input[type="number"]')
              .some(function(el) {
                 return isNumber(el.value); 
              })? 1 : 0; 

or you can do either of the following to coerce true to 1 and false to 0:

var result = Number($('#goodsFilter')
              .find('input[type="number"]')
              .some(function(el) {
                 return isNumber(el.value); 
              }));

or

var result = +($('#goodsFilter')
              .find('input[type="number"]')
              .some(function(el) {
                 return isNumber(el.value); 
              }));

The above is only lightly tested, the optional thisArg parameter might be redundant.

RobG
  • 142,382
  • 31
  • 172
  • 209
  • Oh, thank you for detailed answer. If I need to continue to use `.some()`, then your option will help me to write less and do more. I'm a little confused in choosing the answer – "what is a hack, and what is a feature". However, the code of your implementation `.some()` is more understandable for beginners (and I am) so I switched my vote. Thank you for your attention and time! – trogwar Apr 29 '13 at 14:30
  • You should really do `if (typeof(thisArg) == 'undefined') { thisArg = window; }` before the `for` to avoid that condition in every loop iteration. Inside the loop just use the _`.call` version_ (the `else` one) and it should work the same. – diego nunes Apr 29 '13 at 15:38
  • [ECMA-262](http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.17) says `If a thisArg parameter is provided, it will be used as the this value for each invocation of callbackfn. If it is not provided, undefined is used instead`. So *thisArg* should not be set to *window* if not provided, it must be left undefined. That way it will stay undefined in strict mode and default to window otherwise, and be consistent with Array.prorotype.some and Array.string.some. The extra test is trivial overhead, far less than `$(this[i])`. – RobG Apr 29 '13 at 22:38
  • Note that you can't use `fn.call(thisArg ...)` and leave *thisArg* to whatever it is passed so the method because in ES5, using *call* with a *thisArg* of *undefined* will set *this* to *undefined* in the function, which again is inconsistent with the built–in *some*. – RobG Apr 29 '13 at 22:44
  • But thanks for your input, please test it as thoroughly as you like and post something better if you wish. – RobG Apr 29 '13 at 22:46
5

You could use the .filter method, and then check the length.

$('#goodsFilter')
    .find('input[type="number"]')
    .filter(function(i,el){ return isNumber($(el).val())); })
    .length > 0
codebox
  • 19,927
  • 9
  • 63
  • 81
xdazz
  • 158,678
  • 38
  • 247
  • 274
  • Thanks, @xdazz for answer! But why a should use .filter()? ` var n = $('#goodsFilter') .find('input[type="number"]') .filter(function(el){ console.log(el); return isNumber($(el).val()); }) .length > 0; console.log(n); ` shows me "TypeError: 'undefined' is not an object (evaluating 'o.nodeName.toLowerCase')" in "jquery.js". Thanks for an idea using .length! It's simple and useful for me (: – trogwar Apr 29 '13 at 03:13
  • @RobG, .some() do not works. Totally for me (see error in post). – trogwar Apr 29 '13 at 03:15
  • Then you'll need a "shim" to provide it. Your question just says "it does not work" without saying what "it" is. – RobG Apr 29 '13 at 03:19
  • I use shim like this: `` in `` tag, and jquery and my script – right before ``. How can i improve my question? – trogwar Apr 29 '13 at 03:26
  • This code should return 1 if any element - number. Otherwise - 0. And I am getting an error when try to use .some(). .each() returns only the array elements, not one boolean value. – trogwar Apr 29 '13 at 03:31
  • This won't be the same in terms of performance. `some` terminates once it finds a match; `filter` will not. Unless the matching term is the last item in the array, this method will always be slower. You'd be better off writing your own extension to do this if you're not dealing with a trivially small array. – Lovethenakedgun Feb 03 '20 at 07:34
3

$(...).is(function) should work too. The jQuery API documentation states (emphasis mine):

Check the current matched set of elements against a selector, element, or jQuery object and return true if at least one of these elements matches the given arguments.

So using the example in the question, we would have something like:

var result = $('#goodsFilter')
              .find('input[type="number"]')
              .is(function(idx, el) {
                  return isNumber(el.value); 
              })? 1 : 0; 
Dyson Lu
  • 41
  • 2
  • 1
    Keep in mind, this function keeps checking even after it has found at least 1 true; *some* stops after a match has been found. – Kade Jul 02 '21 at 14:47
2

. . In the most basic version, you can just create a "some" function:

function eSome(arr, f) { var i = 0, n = arr.length;
  for (;i<n;i++) { if (!i in arr) { continue }
    if (f(i, arr[i])) { return true; }
  } return false;
}

var list = [0, 1, 2, 3, 4, 5];
var testFunction = function (i, e) { return e === 2; };
console.log(eSome(list, testFunction));
//returns true and the loop ran only for the necessary three times.

. . If you want to chain the .some call in a jQuery object, you can add it as a jQuery function as well, using something like this (now tested and fixed) example:

jQuery.fn.some = function (f) { var i = 0, n = this.length;
  for (;i<n;i++) { if (!i in this) { continue }
    if (f(i, this[i])) { return true; }
  }
  return false;
}

$('.a').some(function (i, el) { return ($(el).text() == 'weeee!'); });

. . As @RobG pointed out in the comments, the native Array.prototype.some implementation calls your callback with a different set of parameters. I'm following the OP's sample code, but you can mimic the ECMA implementation's parameter with if (f(this[i], i, this)) { return true; } inside the loop.

. . You can also shim it on Array.prototype.some, but I strongly advise against any direct modifications to the built-in prototypes.

diego nunes
  • 2,750
  • 1
  • 14
  • 16
  • Great, this is just what i wanted! Chaining looks more attractive. – trogwar Apr 29 '13 at 04:11
  • 2
    Why are you using `for-in`? Do you understand what that does in JavaScript? Also, you're creating an implicit global variable `l`. –  Apr 29 '13 at 04:18
  • 1
    It would probably be more consitent with other jQuery callbacks to use `l.get(i)` instead of `l.eq(i)`, and then the example would use `$(el).text()`. – Paul Apr 29 '13 at 04:18
  • @squint I'm not really a _jQuery guy_, so I used the "for in" as I would in an array (which have no problems at all). Good catch pointing out that it may cause problems with a _jQuery Object_ and the undeclared variable. I fixed those, thanks. @Paulpro My idea was to return a _jQuery Object_ in the loop so the user won't have to call `$()` again in every iteration. – diego nunes Apr 29 '13 at 04:55
  • @DiegoNunes Yeah, it's more efficient if the object is going to be converted back to a jQuery object anyway, but it less consistent with the jQuery builtin functions that have callbacks. Anyway, my downvote was for the global `l` variable and you fixed that, so I switched my vote. – Paul Apr 29 '13 at 05:36
  • @Paulpro I will let the code as it is because I really think it is a huge (micro)optimization by itself, but the comment about the API consistency was a nice addition and it warns any future visitor more used to jQuery built-in functions about this difference. Thanks :) – diego nunes Apr 29 '13 at 06:39
  • @DiegoNunes: Well, there is a problem with `for-in` on Arrays in that you don't get a guaranteed order of enumeration. Sometimes that's not an issue, but sometimes it is. –  Apr 29 '13 at 12:15
  • 1
    @DiegoNunes—don't suggest to "shim" *Array.prototype*, there is an *Array.prototype.some* already. Issues with the above: 1) the order of arguments is opposite the order in existing *some* methods 2) the existing *some* methods return *true* and *false*, not 1 and 0 3) the native methods only visit existing members, the above assumes a contiguous set of members from 0 to *length* 4) there is no point to assign `l = this`, just use *this* 5) it's more efficient to use `this[i]` than `l.eq(i)` or `l.get(i)` (it's what both *eq* and *get* use internally and removes an unnecessary function call). – RobG Apr 29 '13 at 13:15
  • @RobG The idead of a shim is to provide (more or less) equivalent functionality when it's not natively present. Of course it would only apply to old browsers (you can check with `if (!Array.prototype.some)` before defining the new function). 1) I followed the OP code sample arguments' order, didn't really try to mimic the ECMA some, but that is a nice addition to the answer. 2) My method already returns _true_/_false_ as far as I can see. 3) That makes perfect sense. Just fixed that, thanks. 4) Fixed. 5) Actually, the `.eq` call returns a new jQuery Object, not a default DOM Object. – diego nunes Apr 29 '13 at 14:06
  • @RobG 5) The `.eq` actually returns a new jQuery Object rather than the default DOM object. As that is being used inside jQuery I though it would make sense that the user would immediately have access to jQuery functions on the objects without having to use `$()`. – diego nunes Apr 29 '13 at 14:07
  • Cool. If the function is to be generic, it should pass `this[i]` and let the callback function call `$()` if it wants. If it's to be specific to jQuery, it should pass `$(this[i])`. I just hate the cost of the *$* function if it's not necessary. – RobG Apr 29 '13 at 14:14
  • @Paulpro have pointed out that even the default jQuery functions implement the callbacks with the native object and not a jQuery one. I didn't know that the "eq" would internally call the `$()` (and the new `$()` call was exactly what I was trying to avoid in the OP original code). If that's the case, I think you are both right and the code should return the raw DOM object. – diego nunes Apr 29 '13 at 14:20
0

Implementation in Vanilla Javascript( with arrow syntax)

function some(arr,callback){
  for(let i=0;i<arr.length;i++){
    if(callback(arr[i],i,arr)){
      return true;
    }
  }
  return false;
}

some use:

check if an array has an even number:

function hasEvenNum(arr){
  return arr.some(value=>{
    return value % 2 === 0;
  });
}
Carmine Tambascia
  • 1,628
  • 2
  • 18
  • 32
0

Try to use [].prototype.call method, the first argument will be an Array-like value, the second one is a function that will be called on each element.

[].some.call($('#goodsFilter').find('input[type="number"]'), function (el) {
    return isNumber($(el).val());
});
Văn Quyết
  • 2,384
  • 14
  • 29