Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
David Del Prado committed Oct 9, 2018
1 parent 3896db9 commit cbcec24
Show file tree
Hide file tree
Showing 22 changed files with 2,127 additions and 0 deletions.
20 changes: 20 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"env": {
"browser": true
},
"extends": [
"standard"
],
"rules": {
"no-console": ["error", { "allow": ["warn", "error"] }]
},
"globals": {
"_": true,
"after": true,
"afterEach": true,
"before": true,
"beforeEach": true,
"describe": true,
"it": true
}
}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules
22 changes: 22 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
(The MIT License)

Copyright (c) 2018 Geoblink

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.
42 changes: 42 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# lodash-mixins

A collection of functionalities to extend lodash library.

## Installation

`yarn add --save lodash-mixins`

## Usage

In your app:

```javascript
const _ = require('lodash')
require('lodash-mixins')(_)
```

## API

### fromPairsMap

`fromPairsMap (collection, iteratee)` Applies fromPairs to the result of mapping the given iteratee to the given array.

### getTruthyKeys

`function getTruthyKeys (collection, parseKeyFunction)` Returns the truthy values of the collection. If `parseKeyFunction` is provided, the values will be parsed against it.

### hasTruthyValues

`function hasTruthyValues (object)` Returns whether given object has at least one truthy value for one of its keys.

### mapNonNil

`function mapNonNil (collection, iteratee)` Returns a new collection with non-nil values.

### mGet

`function mGet (object, arrayOfKeys, defaultValue)` Returns a new array with the values of `object` that match `arrayOfKeys`

### shortcuttedReduce

`function shortcuttedReduce (collection, iteratee, accumulator)` Like _.reduce but as soon as accumulator changes it finishes execution.
26 changes: 26 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
var fromPairsMap = require('./src/fromPairsMap')
var getTruthyKeys = require('./src/getTruthyKeys')
var hasTruthyValues = require('./src/hasTruthyValues')
var mapNonNil = require('./src/mapNonNil')
var mergeForEach = require('./src/mergeForEach')
var mGet = require('./src/mGet')
var shortcuttedReduce = require('./src/shortcuttedReduce')

/**
* Extends Lodash with additional functionality.
*
* @param {lodash} _ Object Lodash instance to be extended
*/
module.exports = function (_) {
_.mixin({
fromPairsMap: fromPairsMap,
getTruthyKeys: getTruthyKeys,
hasTruthyValues: hasTruthyValues,
mapNonNil: mapNonNil,
mergeForEach: mergeForEach,
mGet: mGet,
shortcuttedReduce: shortcuttedReduce
})

return _
}
34 changes: 34 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"name": "lodash-mixins",
"version": "1.0.0",
"description": "Extends Lodash with additional functionality",
"main": "index.js",
"author": "Geoblink",
"scripts": {
"test": "mocha"
},
"keywords": [
"lodash",
"mixin",
"utils"
],
"license": "MIT",
"devDependencies": {
"chai": "^4.2.0",
"eslint": "^5.6.1",
"eslint-config-standard": "^12.0.0",
"eslint-plugin-import": "^2.14.0",
"eslint-plugin-node": "^7.0.1",
"eslint-plugin-promise": "^4.0.1",
"eslint-plugin-standard": "^4.0.0",
"mocha": "^5.2.0",
"sinon": "^6.3.5",
"sinon-chai": "^3.2.0"
},
"peerDependencies": {
"lodash": "^4.17.11"
},
"dependencies": {
"jsdoc": "^3.5.5"
}
}
17 changes: 17 additions & 0 deletions src/constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/**
* -1|0|1
* @typedef {Number} SortingOrder
*/

/**
* Possible results when comparing two items.
* @readonly
* @enum {SortingOrder}
*/
var SORTING_ORDER = {
LHS_BEFORE_RHS: -1,
LHS_AFTER_RHS: 1,
EQUAL: 0
}

module.exports = { SORTING_ORDER }
12 changes: 12 additions & 0 deletions src/fromPairsMap.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
var _ = require('lodash')

/**
* Applies fromPairs to the result of mapping the given iteratee to the given array.
*
* @param {Array|Object} collection The collection to iterate over.
* @param {Function} iteratee The function invoked per iteration.
* @returns {Object} Returns the new object.
*/
module.exports = function fromPairsMap (collection, iteratee) {
return _.fromPairs(_.map(collection, iteratee))
}
17 changes: 17 additions & 0 deletions src/getTruthyKeys.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
var _ = require('lodash')

/**
* Gets the truthy values of the collection.
*
* @param {Array|Object} collection The collection to iterate over.
* @param {Function} parseKeyFunction The function invoked per iteration.
* @returns {Object} Returns the new filtered collection.
*/
module.exports = function getTruthyKeys (collection, parseKeyFunction) {
var isFunction = _.isFunction(parseKeyFunction)
return _.filter(_.map(collection, function (value, key) {
if (value) {
return isFunction ? parseKeyFunction(key) : key
}
}))
}
13 changes: 13 additions & 0 deletions src/hasTruthyValues.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
var shortcuttedReduce = require('./shortcuttedReduce')

/**
* Returns whether given object has at least one truthy value for one of its keys.
*
* @param {Object} object Object to be checked.
* @returns {Boolean} `true` if there's at least one value which is truthy.
*/
module.exports = function hasTruthyValues (object) {
return shortcuttedReduce(object, function (accum, value) {
return accum || !!value
})
}
13 changes: 13 additions & 0 deletions src/mGet.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
var _ = require('lodash')

/**
* Multiple get.
*
* @param {Object} object The object to query.
* @param {Array} arrayOfKeys Array with the paths of the properties to get.
* @param {*} defaultValue The value returned for undefined resolved values.
* @return {Array} Returns the new mapped array.
*/
module.exports = function mGet (object, arrayOfKeys, defaultValue) {
return _.map(arrayOfKeys, function (key) { return _.get(object, key, defaultValue) })
}
16 changes: 16 additions & 0 deletions src/mapNonNil.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
var _ = require('lodash')

/**
* Map returning only non-nil elements
*
* @param {Array|Object} collection The collection to iterate over.
* @param {Function} iteratee The function invoked per iteration.
* @returns {Array} Returns the new mapped array.
*/
module.exports = function mapNonNil (collection, iteratee) {
return _.filter(_.map(collection, iteratee), isNotNil)

function isNotNil (item) {
return !_.isNil(item)
}
}
131 changes: 131 additions & 0 deletions src/mergeForEach.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
var _ = require('lodash')
var { SORTING_ORDER } = require('./constants')

/**
* @template I
* @typedef {string|string[]|function(I): any} Iteratee
*/

/**
* @template LHSItem
* @template RHSItem
* @typedef {object} MergeComparatorParams
* @property {LHSItem} lhsItem Left-hand-side collection item.
* @property {RHSItem} rhsItem Right-hand-side collection item.
* @property {function(LHSItem): any} getLHSValue Function to get value used to
* sort left-hand-side collection.
* @property {function(RHSItem): any} getRHSValue Function to get value used to
* sort right-hand-side collection.
*/

/**
* Divide-and-conquer-based for-each function where a different iteratee is
* called for items in both collections, items in the left one but not in the
* right one and items in the right one but not in the left one.
*
* Can be used to implement efficient algorithms which profit from sorted data
* like `mergeSort` or `mergeJoinWith`.
*
* Both collections must be sortable. They will be sorted ascendently using
* value returned by the corresponding iteratee.
*
* @template LHSItem
* @template RHSItem
* @param {{[key: string]: LHSItem}|LHSItem[]} lhs A collection of elements.
* @param {{[key: string]: RHSItem}|RHSItem[]} rhs A collection of elements.
* @param {object} options Options for comparison.
* @param {Iteratee<LHSItem>} options.lhsIteratee Iteratee used to get the
* value used to sort `lhs`. Returned value will be used to sort the collection
* before running the divide-and-conquer algorithm.
* @param {Iteratee<RHSItem>} options.rhsIteratee Iteratee used to get the
* value used to sort `rhs`. Returned value will be used to sort the collection
* before running the divide-and-conquer algorithm.
* @param {?function(LHSItem, RHSItem)} options.innerCallback Callback called
* when there are two matching elements. Boths elements are passed as arguments.
* @param {?function(LHSItem)} options.leftCallback Callback called when there
* are elements in the left-hand-side collection which cannot be matched with
* any element of the right-hand-side collection.
* @param {?function(RHSItem)} options.rightCallback Callback called when there
* are elements in the right-hand-side collection which cannot be matched with
* any element of the left-hand-side collection.
* @param {function(MergeComparatorParams<LHSItem, RHSItem>): number} comparator Function
* used to compare an item of `lhs` collection against an item of `rhs`
* collection. Negative values mean that `lhs` item is **before** `rhs` item,
* positive values that `lhs` item is **after** `rhs` item and `0` that both
* items are equivalent in terms of sorting. Default implementation is
* equivalent to `<` operator. Will receive as 3rd and 4th parameters the
* iteratees used to get sorting value for `lhs` and `rhs`.
*/
module.exports = function mergeForEach (lhs, rhs, {
lhsIteratee = function (lhsItem) { return lhsItem },
rhsIteratee = function (rhsItem) { return rhsItem },
innerCallback = function () {},
leftCallback = function () {},
rightCallback = function () {},
comparator = function ({ lhsItem, rhsItem, getLHSValue, getRHSValue }) {
var lhsValue = getLHSValue(lhsItem)
var rhsValue = getRHSValue(rhsItem)

if (lhsValue < rhsValue) {
return SORTING_ORDER.LHS_BEFORE_RHS
} else if (lhsValue > rhsValue) {
return SORTING_ORDER.LHS_AFTER_RHS
} else {
return SORTING_ORDER.EQUAL
}
}
}) {
/** @type {LHSItem[]} */
var sortedLHS = _.sortBy(lhs, lhsIteratee)
/** @type {RHSItem[]} */
var sortedRHS = _.sortBy(rhs, rhsIteratee)

var lhsIndex = 0
var rhsIndex = 0

var getLHSValue = getterFromIteratee(lhsIteratee)
var getRHSValue = getterFromIteratee(rhsIteratee)

while (lhsIndex < sortedLHS.length && rhsIndex < sortedRHS.length) {
var lhsItem = sortedLHS[lhsIndex]
var rhsItem = sortedRHS[rhsIndex]

/** @type {MergeForEachOrder} */
var comparisonResult = comparator({
lhsItem,
rhsItem,
getLHSValue,
getRHSValue
})

if (comparisonResult < SORTING_ORDER.EQUAL) {
leftCallback(lhsItem)
lhsIndex++
} else if (comparisonResult > SORTING_ORDER.EQUAL) {
rightCallback(rhsItem)
rhsIndex++
} else {
innerCallback(lhsItem, rhsItem)
lhsIndex++
rhsIndex++
}
}

while (lhsIndex < sortedLHS.length) {
leftCallback(sortedLHS[lhsIndex])
lhsIndex++
}

while (rhsIndex < sortedRHS.length) {
rightCallback(sortedRHS[rhsIndex])
rhsIndex++
}

function getterFromIteratee (iteratee) {
return _.isFunction(iteratee)
? iteratee
: function (item) {
return _.get(item, iteratee)
}
}
}
Loading

0 comments on commit cbcec24

Please sign in to comment.