diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..7586723 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,9 @@ +{ + "env": { + "node": true, + "browser": true + }, + "rules": { + "quotes": [2, "single"] + } +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3b49653 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +.DS_Store +*.log +bower_components/ +build/ +components/ +node_modules/ +coverage/ diff --git a/.jscs.json b/.jscs.json new file mode 100644 index 0000000..d0048b5 --- /dev/null +++ b/.jscs.json @@ -0,0 +1,135 @@ +{ + "requireCurlyBraces": [ + "if", + "else", + "for", + "while", + "do", + "try", + "catch" + ], + "requireSpaceAfterKeywords": [ + "if", + "else", + "for", + "while", + "do", + "switch", + "return", + "try", + "catch" + ], + "requireSpaceBeforeBlockStatements": true, + "requireParenthesesAroundIIFE": true, + "requireSpacesInConditionalExpression": true, + "requireSpacesInFunctionExpression": { + "beforeOpeningCurlyBrace": true + }, + "requireSpacesInAnonymousFunctionExpression": { + "beforeOpeningRoundBrace": true, + "beforeOpeningCurlyBrace": true + }, + "requireSpacesInNamedFunctionExpression": { + "beforeOpeningRoundBrace": true, + "beforeOpeningCurlyBrace": true + }, + "requireSpacesInFunctionExpression": { + "beforeOpeningCurlyBrace": true + }, + "requireMultipleVarDecl": true, + "requireBlocksOnNewline": true, + "disallowPaddingNewlinesInBlocks": true, + "disallowEmptyBlocks": true, + "disallowSpacesInsideObjectBrackets": true, + "disallowSpacesInsideArrayBrackets": true, + "disallowSpacesInsideParentheses": true, + "requireSpacesInsideObjectBrackets": "all", + "disallowDanglingUnderscores": true, + "disallowSpaceAfterObjectKeys": true, + "requireCommaBeforeLineBreak": true, + "requireOperatorBeforeLineBreak": [ + "?", + "+", + "-", + "/", + "*", + "=", + "==", + "===", + "!=", + "!==", + ">", + ">=", + "<", + "<=" + ], + "requireSpaceBeforeBinaryOperators": [ + "=", + ",", + "+", + "-", + "/", + "*", + "==", + "===", + "!=", + "!==" + ], + "requireSpaceAfterBinaryOperators": [ + "=", + ",", + "+", + "-", + "/", + "*", + "==", + "===", + "!=", + "!==" + ], + "disallowSpaceAfterPrefixUnaryOperators": ["++", "--", "+", "-", "~", "!"], + "disallowSpaceBeforePostfixUnaryOperators": ["++", "--"], + "requireSpaceBeforeBinaryOperators": [ + "+", + "-", + "/", + "*", + "=", + "==", + "===", + "!=", + "!==" + ], + "requireSpaceAfterBinaryOperators": [ + "+", + "-", + "/", + "*", + "=", + "==", + "===", + "!=", + "!==" + ], + "disallowImplicitTypeConversion": ["numeric", "boolean", "binary", "string"], + "requireCamelCaseOrUpperCaseIdentifiers": true, + "disallowKeywords": ["with"], + "disallowMultipleLineStrings": true, + "disallowMultipleLineBreaks": true, + "validateLineBreaks": "LF", + "validateQuoteMarks": "'", + "disallowMixedSpacesAndTabs": true, + "disallowTrailingWhitespace": true, + "disallowTrailingComma": true, + "disallowKeywordsOnNewLine": ["else"], + "requireLineFeedAtFileEnd": true, + "maximumLineLength": 78, + "requireCapitalizedConstructors": true, + "requireDotNotation": true, + "disallowYodaConditions": true, + "validateJSDoc": { + "checkParamNames": true, + "checkRedundantParams": true, + "requireParamTypes": true + } +} diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..4efa05e --- /dev/null +++ b/.npmignore @@ -0,0 +1,11 @@ +.* +*.log +build/ +bower_components/ +components/ +coverage/ +node_modules/ +test.js +bower.json +component.json +History.md diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..c3e56ad --- /dev/null +++ b/.travis.yml @@ -0,0 +1,13 @@ +language: node_js +script: npm run-script test-travis +node_js: +- '0.10' +- '0.11' +after_script: npm install coveralls@2.10.0 && cat ./coverage/lcov.info | coveralls +deploy: + provider: npm + email: tituswormer@gmail.com + api_key: + secure: EpsOgmqYn+7QvS0BPJfb24oRuXZnxFaXkpW2R1G0yNe06lI5Gjx/r/oSbRoTzWgzo2MGIeGArF4Gbi1uAZSNEsUCAzikounQzLjlzkKq6Emy/xac5Q7qCiaY2BgvIwHsd3bdp1XPOMnUK3iQRfoRyvHhOOrTOZAZK1I+dTkUxL0= + on: + repo: wooorm/array-iterate diff --git a/History.md b/History.md new file mode 100644 index 0000000..e69de29 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..0c06d5b --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +(The MIT License) + +Copyright (c) 2014 Titus Wormer + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Readme.md b/Readme.md new file mode 100644 index 0000000..c95904f --- /dev/null +++ b/Readme.md @@ -0,0 +1,58 @@ +# array-iterate [![Build Status](https://img.shields.io/travis/wooorm/array-iterate.svg?style=flat)](https://travis-ci.org/wooorm/array-iterate) [![Coverage Status](https://img.shields.io/coveralls/wooorm/array-iterate.svg?style=flat)](https://coveralls.io/r/wooorm/array-iterate?branch=master) + +[`Array#forEach()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach) with the possibility to change the next position. + +## Installation + +npm: +```sh +$ npm install array-iterate +``` + +Component.js: +```sh +$ component install wooorm/array-iterate +``` + +Bower: +```sh +$ bower install array-iterate +``` + +## Usage + +````js +var iterate = require('array-iterate'); +var isFirst = true; +var context = 'iterate'; + +iterate([1, 2, 3, 4], function (value, index, values) { + console.log(this, value, index, values) + + if (isFirst && index + 1 === values.length) { + isFirst = false; + + return 0; + } +}, context); +/** + * [String: 'iterate'], 1, 0, [ 1, 2, 3, 4 ] + * [String: 'iterate'], 2, 1, [ 1, 2, 3, 4 ] + * [String: 'iterate'], 3, 2, [ 1, 2, 3, 4 ] + * [String: 'iterate'], 4, 3, [ 1, 2, 3, 4 ] + * [String: 'iterate'], 1, 0, [ 1, 2, 3, 4 ] + * [String: 'iterate'], 2, 1, [ 1, 2, 3, 4 ] + * [String: 'iterate'], 3, 2, [ 1, 2, 3, 4 ] + * [String: 'iterate'], 4, 3, [ 1, 2, 3, 4 ] + */ +```` + +## API + +### iterate(arrayLike, callback, context) + +Functions just like [`Array#forEach()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach), but when `callback` returns a `number`, iterates over the item at `number` next. + +## License + +MIT © Titus Wormer diff --git a/bower.json b/bower.json new file mode 100644 index 0000000..c6b3898 --- /dev/null +++ b/bower.json @@ -0,0 +1,31 @@ +{ + "name": "array-iterate", + "main": "index.js", + "description": "forEach with the possibility to change the next position", + "license": "MIT", + "keywords": [ + "array", + "list", + "iterate", + "walk" + ], + "repository": { + "type": "git", + "url": "https://github.com/wooorm/array-iterate.git" + }, + "authors": [ + "Titus Wormer " + ], + "ignore": [ + ".*", + "*.log", + "*.md", + "build/", + "components/", + "coverage/", + "node_modules/", + "component.json", + "package.json", + "test.js" + ] +} diff --git a/component.json b/component.json new file mode 100644 index 0000000..8809ae9 --- /dev/null +++ b/component.json @@ -0,0 +1,16 @@ +{ + "name": "array-iterate", + "version": "0.0.1", + "description": "forEach with the possibility to change the next position", + "license": "MIT", + "keywords": [ + "array", + "list", + "iterate", + "walk" + ], + "repository": "wooorm/array-iterate", + "scripts" : [ + "index.js" + ] +} diff --git a/index.js b/index.js new file mode 100644 index 0000000..2ae68ec --- /dev/null +++ b/index.js @@ -0,0 +1,85 @@ +'use strict'; + +/** + * Cache `hasOwnProperty`. + */ + +var has; + +has = Object.prototype.hasOwnProperty; + +/** + * `Array#forEach()` with the possibility to change + * the next position. + * + * @param {{length: number}} values + * @param {function(*, number, {length: number}): number|undefined} callback + * @param {*} context + */ + +function iterate(values, callback, context) { + var index, + result; + + if (!values) { + throw new Error( + 'TypeError: Iterate requires that |this| ' + + 'not be ' + values + ); + } + + if (!has.call(values, 'length')) { + throw new Error( + 'TypeError: Iterate requires that |this| ' + + 'has a `length`' + ); + } + + if (typeof callback !== 'function') { + throw new Error( + 'TypeError: callback must be a function' + ); + } + + index = -1; + + /** + * The length might change, so we do not cache it. + */ + + while (++index < values.length) { + /** + * Skip missing values. + */ + + if (!(index in values)) { + continue; + } + + result = callback.call(context, values[index], index, values); + + /** + * If `callback` returns a `number`, move `index` over to + * `number`. + */ + + if (typeof result === 'number') { + /** + * Make sure that negative numbers do not + * break the loop. + */ + + if (result < 0) { + index = 0; + } + + index = result - 1; + } + } +} + +/** + * Expose `iterate`. + */ + +module.exports = iterate; diff --git a/package.json b/package.json new file mode 100644 index 0000000..1ac290b --- /dev/null +++ b/package.json @@ -0,0 +1,33 @@ +{ + "name": "array-iterate", + "version": "0.0.1", + "description": "forEach with the possibility to change the next position", + "license": "MIT", + "keywords": [ + "array", + "list", + "iterate", + "walk" + ], + "repository": { + "type": "git", + "url": "https://github.com/wooorm/array-iterate.git" + }, + "author": "Titus Wormer ", + "devDependencies": { + "eslint": "^0.9.0", + "istanbul": "^0.3.0", + "jscs": "^1.0.0", + "mocha": "^2.0.0" + }, + "scripts": { + "test": "node_modules/.bin/_mocha --reporter spec --check-leaks -u exports test.js", + "test-travis": "node_modules/.bin/istanbul cover node_modules/.bin/_mocha --report lcovonly -- --reporter spec --check-leaks -u exports test.js", + "coverage": "node_modules/.bin/istanbul cover node_modules/.bin/_mocha -- -- test.js", + "lint": "npm run-script lint-api && npm run-script lint-test && npm run-script lint-style", + "lint-api": "node_modules/.bin/eslint index.js", + "lint-test": "node_modules/.bin/eslint test.js --env mocha", + "lint-style": "node_modules/.bin/jscs index.js test.js --reporter=inline", + "make": "npm run-script lint && npm run-script coverage" + } +} diff --git a/test.js b/test.js new file mode 100755 index 0000000..1231c41 --- /dev/null +++ b/test.js @@ -0,0 +1,158 @@ +'use strict'; + +/** + * Dependencies. + */ + +var iterate, + assert; + +iterate = require('./'); +assert = require('assert'); + +/** + * Tests. + */ + +describe('iterate()', function () { + it('should be a `function`', function () { + assert(typeof iterate === 'function'); + }); + + it('should throw when no `values` are given', function () { + assert.throws(function () { + iterate(); + }, /\|this\| not be undefined/); + }); + + it('should throw when an object without `length` is given', function () { + assert.throws(function () { + iterate({}); + }, /\|this\| has a `length`/); + }); + + it('should throw when no `callback` is given', function () { + assert.throws(function () { + iterate([]); + }, /must be a function/); + }); + + it('should iterate over an array with `value`, `index`, and `values`', + function () { + var list, + n; + + list = [0, 1, 2]; + + n = 0; + + iterate(list, function (value, index, values) { + assert(value === n); + assert(index === n); + assert(values === list); + + n++; + }); + + assert(n === 3); + } + ); + + it('should invoke `callback` with `undefined` as context', function () { + var n; + + n = 0; + + iterate([1, 2, 3], function () { + assert(this === undefined); + + n++; + }); + + assert(n === 3); + }); + + it('should invoke `callback` with the given `context`', function () { + var scope, + n; + + scope = this; + + n = 0; + + iterate([1, 2, 3], function () { + assert(this === scope); + + n++; + }, scope); + + assert(n === 3); + }); + + it('should change the next position when `callback` returns a number', + function () { + var n; + + n = 0; + + iterate([0, 1, 2], function (value, index) { + n++; + + assert(value === index); + + /** + * Stay on position `0` ten times. + */ + + if (n <= 10) { + return 0; + } + }); + + assert(n === 13); + } + ); + + it('should ignore missing values', function () { + var list, + magicNumber, + n; + + magicNumber = 10; + + list = Array(magicNumber); + + list.push(magicNumber + 1); + + iterate(list, function (value, index) { + assert(value === magicNumber + 1); + + assert(index === magicNumber); + + n = index; + }); + + assert(n === magicNumber); + }); + + it('should NOT fail when a negative index is returned', function () { + var results, + n; + + n = 0; + + results = ['a', 'b', 'a', 'b', 'c', 'd']; + + iterate(['a', 'b', 'c', 'd'], function (value) { + assert(value === results[n]); + + n++; + + if (n === 2) { + return -1; + } + }); + + assert(n === results.length); + }); +});