Skip to content


Make _.include/_.contains work with strings. #667

wants to merge 2 commits into from

4 participants


This makes _.include() / _.contains() work with strings in addition to collections.

Note String.contains is a proposed method for Harmony: – also it's really just another indexOf check, so it seems to make sense.

String.indexOf is present in Javascript 1.0, so a method identity check seemed unnecessary.

I wasn't sure where a doc change would go.


Also, this allows Javascript to naturally coerce the search item to a string. Arguably that's different than how _.include works with Arrays (since it uses === which does not allow this kind of coercion). This is the simplest implementation, but I'd also be comfortable with a type check on target (returning false if the target is not a string).


I also can't figure out why this works with boxed strings, though it seems to (like new String('test')), since typeof new String('test') == 'object'


I also can't figure out why this works with boxed strings, though it seems to (like new String('test')), since typeof new String('test') == 'object'.

Use _.isString instead of typeof.


Strings are array-like so that's why this works in modern browsers on the edge version of Underscore.

_.contains('test', 'e'); // => true

_.contains (in current underscore) didn't seem to work because of the test if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1; where nativeIndexOf = ArrayProto.indexOf


_contains('test', 'es'); // => false

_.contains('test', 'es'); // => false

Yap, that's because being array-like means having a length and index properties like "hi"[1]; // i.
That said I dig me some ES6 sugar :P


Closing as discussed in #668...

@jashkenas jashkenas closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jul 11, 2012
  1. @ianb

    Make _.include/_.contains work with strings. This matches the upcomin…

    ianb committed
    …g String.contains method.
  2. @ianb

    Fix test for strings (to also catch boxed strings). And apparently th…

    ianb committed
    …e test related to boxed strings was backwards.
This page is out of date. Refresh to see the latest.
Showing with 9 additions and 2 deletions.
  1. +5 −0 test/collections.js
  2. +4 −2 underscore.js
5 test/collections.js
@@ -164,6 +164,11 @@ $(document).ready(function() {
ok(!_.include([1,3,9], 2), 'two is not in the array');
ok(_.contains({moe:1, larry:3, curly:9}, 3) === true, '_.include on objects checks their values');
ok(_([1,2,3]).include(2), 'OO-style include');
+ ok(_.include('test this out', 'out'), 'include with a string');
+ ok(!_.include('test this out', 'not included'), 'include with a string (false)');
+ ok(_.include('1 2 3 4', 3), 'include with a string and number');
+ ok(_.include(new String('test this'), 'test'), 'works with boxed strings');
+ ok(_('test this out').include('this'), 'OO-style include with a string');
test('collections: invoke', function() {
6 underscore.js
@@ -198,11 +198,13 @@
// Determine if a given value is included in the array or object using `===`.
- // Aliased as `contains`.
+ // Aliased as `contains`. Or if `obj` is a string, test if the string contains the
+ // substring.
_.include = _.contains = function(obj, target) {
var found = false;
if (obj == null) return found;
if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
+ if (_.isString(obj)) return obj.indexOf(target) != -1;
found = any(obj, function(value) {
return value === target;
@@ -832,7 +834,7 @@
return == '[object ' + name + ']';
// Define a fallback version of the method in browsers (ahem, IE), where
// there isn't any inspectable "Arguments" type.
if (!_.isArguments(arguments)) {
Something went wrong with that request. Please try again.