UNPKG

@onesy/utils

Version:
130 lines (107 loc) 4.81 kB
import is from './is'; import unique from './unique'; import permutation from './permutation'; import variationWithRepetition from './variationWithRepetition'; const optionsDefault = { response: 'array' }; // m - array, n - items // m! / (m - n)! export default function variation(value_) { let items_ = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; let options_ = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; const options = { ...optionsDefault, ...options_ }; if (is('array', value_)) { const value = unique(value_); const items = is('number', items_) ? items_ : 0; const length = value.length; if (items < 1) return [value]; if (items === 1) return value.map(item_ => [item_]); // If items is same is length it's a permutation method if (length === items) return permutation(value, options); // If items is more than length it's a variationWithRepetition method if (items > length) return variationWithRepetition(value, items, options); // And other use case, we have less items than the amount of values // [0, 1, 2, 3, 4], or whatever const allIndexes = [...Array(length).keys()]; let item = [...Array(items).keys()]; let index = items - 2; let availableIndexes = allIndexes.filter(i_ => item.slice(0, -1).indexOf(i_) === -1); const availableIndexesLength = availableIndexes.length; let updated = false; const response = []; if (options.response === 'array') { first: while (index >= 0) { if (updated) { index = items - 2; updated = false; } // Update available indexes availableIndexes = allIndexes.filter(i_ => item.slice(0, -1).indexOf(i_) === -1); // Add all the values from available indexes to the last index for (let l = 0; l < availableIndexesLength; l++) { item[item.length - 1] = availableIndexes[l]; const item_ = item.slice().map(index_ => value[index_]); response.push(item_); } // Move to the left of the values while (true) { if (item[index] === length - 1) { index--; if (index < 0) break; updated = true; } else { // Make this value a next one in available based on it's current and all values from the left item[index] = allIndexes.filter(i_ => item.slice(0, index + 1).indexOf(i_) === -1 && i_ > item[index])[0]; if (item[index] === undefined) { index--; if (index < 0) break; updated = true; } else { // Make rest of item values unique based on all the left values const part = item.slice(0, index + 1); item = [...part, ...allIndexes.filter(i_ => part.indexOf(i_) === -1).slice(0, items - part.length)]; if (item[1] === undefined || item[index] === undefined) break first; break; } } } if (item[0] === length && item[1] === length - 1) break; } return response; } if (options.response === 'yield') return function* () { first: while (index >= 0) { if (updated) { index = items - 2; updated = false; } // Update available indexes availableIndexes = allIndexes.filter(i_ => item.slice(0, -1).indexOf(i_) === -1); // Add all the values from available indexes to the last index for (let l = 0; l < availableIndexesLength; l++) { item[item.length - 1] = availableIndexes[l]; const item_ = item.slice().map(index_ => value[index_]); yield item_; response.push(item_); } // Move to the left of the values while (true) { if (item[index] === length - 1) { index--; if (index < 0) break; updated = true; } else { // Make this value a next one in available based on it's current and all values from the left item[index] = allIndexes.filter(i_ => item.slice(0, index + 1).indexOf(i_) === -1 && i_ > item[index])[0]; if (item[index] === undefined) { index--; if (index < 0) break; updated = true; } else { // Make rest of item values unique based on all the left values const part = item.slice(0, index + 1); item = [...part, ...allIndexes.filter(i_ => part.indexOf(i_) === -1).slice(0, items - part.length)]; if (item[1] === undefined || item[index] === undefined) break first; break; } } } if (item[0] === length && item[1] === length - 1) break; } return response; }; } }