Skip to content

Bind: Add Array.includes to bind#7401

Merged
jridgewell merged 30 commits intoampproject:masterfrom
kmh287:#7378_array_contain
Feb 10, 2017
Merged

Bind: Add Array.includes to bind#7401
jridgewell merged 30 commits intoampproject:masterfrom
kmh287:#7378_array_contain

Conversation

@kmh287
Copy link
Copy Markdown
Contributor

@kmh287 kmh287 commented Feb 7, 2017

Adds "Array.includes" to bind. Also adds a polyfill for Array.includes.

@kmh287
Copy link
Copy Markdown
Contributor Author

kmh287 commented Feb 7, 2017

/to @choumx @jridgewell

@kmh287 kmh287 changed the title Bind: Add Array.contains to bind Bind: Add Array.includes to bind Feb 7, 2017
Copy link
Copy Markdown
Contributor

@jridgewell jridgewell left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need tests, explicitly:

  • NaN should find NaN
  • -0 and 0 should find either.
  • undefined should find undefined, not null
  • null should find null, not undefined
  • Any number should find itself
  • Any string should find itself
  • Any boolean should find itself
  • Any object should find explicitly itself

export function install(win) {
if (!win.Array.prototype.includes) {
/*eslint "no-extend-native": 0*/
Object.defineProperty(Array.prototype, 'includes', {value: includes});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just use simple assignment.

throw new TypeError('"this" is null or not defined');
}
const o = Object(this);
const len = o.length >>> 0;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needless cast (>>> 0).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replaced with your version

const n = fromIndex | 0;
let k = Math.max(n >= 0 ? n : len - Math.abs(n), 0);
while (k < len) {
if (o[k] === searchElement) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will fail [NaN].includes(NaN)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replaced with your version

* @param {number} fromIndex
* @returns {boolean}
*/
export function includes(searchElement, fromIndex) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, we can optimize a bit:

function includes(value, fromIndex) { 
  if (value === value) { // Everything but NaN
    return this.indexOf(value, fromIndex) > -1;
  }
  let i = Math.max(n >= 0 ? n : len + n, 0);
  for (; i < this.length; i++) {
    const value = this[i];
    if (value !== value) {
      return true;
    }
  }
  return false;
}

@kmh287
Copy link
Copy Markdown
Contributor Author

kmh287 commented Feb 8, 2017

Added tests

* @param {!Window} win
*/
export function install(win) {
if (!win.Array.includes) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Array.prototype.includes. What's there now is a static method on Array.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

src/polyfills.js Outdated
import {install as installMathSign} from './polyfills/math-sign';
import {install as installObjectAssign} from './polyfills/object-assign';
import {install as installPromise} from './polyfills/promise';
import {install as installArray} from './polyfills/array';
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's rename the file to array-includes to match the other method polyfills.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

*/
export function includes(value, fromIndex) {
/* eslint no-self-compare: 0 */
if (value === value) { // Everything but NaN
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thinking about this more, let's drop this and push all cases into the for loop. We'll need to change it a bit:

const other = this[i];
if (other === value || (value !== value && other !== other)) {
  return true;
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

/**
* Returns true if the element is in the array and false otherwise.
*
* @param {*} searchElement
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/searchElement/value

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.



/**
* Returns true if the element is in the array and false otherwise.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps add an @see annotation with the MDN link.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we're not using that version anymore, I don't think that'll be necessary.


describe('Array.includes', () => {

const arrayWithPrimitives = [false, 17, 'hello world'];
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move this into the it() method. Ditto for other tests below.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

* @param {number} fromIndex
* @returns {boolean}
*/
export function includes(value, fromIndex) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need to be exported? If not, you can use a mock Window object with install() below to access it for testing.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

}
fromIndex = fromIndex || 0;
const len = this.length;
let i = Math.max(fromIndex >= 0 ? fromIndex : len + fromIndex, 0);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You only need to max the false branch of the ternary, right? I think that would be a bit more readable too.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

fromIndex = fromIndex || 0;
const len = this.length;
let i = Math.max(fromIndex >= 0 ? fromIndex : len + fromIndex, 0);
for (; i < len; i++) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason why you're declaring i outside the loop?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't want the line to be too long

* @returns {boolean}
*/
function includes(value, fromIndex) {
/* eslint no-self-compare: 0 */
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this still necessary?

* Returns true if the element is in the array and false otherwise.
*
* @param {*} value
* @param {number} fromIndex
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For optional params:

@param {number=} opt_fromIndex

*/
function includes(value, fromIndex) {
/* eslint no-self-compare: 0 */
fromIndex = fromIndex || 0;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jridgewell Do we support default parameters yet? Looks like Babel/Closure has basic support.

let i = fromIndex >= 0 ? fromIndex : Math.max(len + fromIndex, 0);
for (; i < len; i++) {
const other = this[i];
if (other === value || (value !== value && other !== other)) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a comment explaining value !== value?

Copy link
Copy Markdown
Contributor

@jridgewell jridgewell left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a nice next step is to find anywhere that does Arary#indexOf > -1 (or one of the == -1, >= 0, < 0) and update them to #includes. You'll also need to remove our Array#includes ban.

src/polyfills.js Outdated
import {install as installMathSign} from './polyfills/math-sign';
import {install as installObjectAssign} from './polyfills/object-assign';
import {install as installPromise} from './polyfills/promise';
import {install as installArray} from './polyfills/array-includes';
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: rename to installArrayIncludes

@kmh287
Copy link
Copy Markdown
Contributor Author

kmh287 commented Feb 9, 2017

Can do, but would prefer not to clutter this PR.

@jridgewell
Copy link
Copy Markdown
Contributor

Can do, but would prefer not to clutter this PR.

Yah, I meant as a follow up.

@dreamofabear
Copy link
Copy Markdown

Looks good. Last thing: can you check the impact to file size for v0.js?

*
* @param {*} value
* @param {number=} opt_fromIndex
* @returns {boolean}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit @return

@kmh287
Copy link
Copy Markdown
Contributor Author

kmh287 commented Feb 10, 2017

+222 bytes

@kmh287
Copy link
Copy Markdown
Contributor Author

kmh287 commented Feb 10, 2017

Fixes #7378

@jridgewell jridgewell merged commit cce7bda into ampproject:master Feb 10, 2017
torch2424 pushed a commit to torch2424/amphtml that referenced this pull request Feb 14, 2017
* add array polyfill, whitelist contains function

* install the polyfill

* debuggign test

* typos

* cleanup

* cleanup

* contains -> includes

* more cleanup

* forgot to update one contains to includes

* whitelist

* fix failing test

* pr comments

* added test

* fixed slight issue with the polyfill

* add test

* typo

* pr comments

* cleanup

* cleanup

* pr comments

* ci issues

* more ci issues

* even more ci issues

* update whitelist

* pr comments

* remove ban on includes

* pr comments
mrjoro pushed a commit to mrjoro/amphtml that referenced this pull request Apr 28, 2017
* add array polyfill, whitelist contains function

* install the polyfill

* debuggign test

* typos

* cleanup

* cleanup

* contains -> includes

* more cleanup

* forgot to update one contains to includes

* whitelist

* fix failing test

* pr comments

* added test

* fixed slight issue with the polyfill

* add test

* typo

* pr comments

* cleanup

* cleanup

* pr comments

* ci issues

* more ci issues

* even more ci issues

* update whitelist

* pr comments

* remove ban on includes

* pr comments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants