Другая возможность - рассмотреть возможность создания чего-то вроде приложения ClickOnce на платформе Windows. Это предоставит ссылку на загружаемый исполняемый файл, который будет запускаться в ОС пользователя, но может сделать обратные вызовы в Интернете для отправки и получения данных. Приложение ClickOnce может встроить элемент управления браузером и, таким образом, у вас будет как веб-совместимое приложение, способное напрямую общаться с хранилищем пользователя.
Это не перестановки, см. определения перестановок из Википедии.
Но вы можете добиться этого с помощью рекурсии:
var allArrays = [['a', 'b'], ['c'], ['d', 'e', 'f']]
function allPossibleCases(arr) {
if (arr.length == 1) {
return arr[0];
} else {
var result = [];
var allCasesOfRest = allPossibleCases(arr.slice(1)); // recur with the rest of array
for (var i = 0; i < allCasesOfRest.length; i++) {
for (var j = 0; j < arr[0].length; j++) {
result.push(arr[0][j] + allCasesOfRest[i]);
}
}
return result;
}
}
Вы также можете сделать это с петлями, но это будет немного сложно, и вам потребуется реализовать свой собственный аналог стека.
Вам не нужна рекурсия или сильно вложенные петли или даже для создания / хранения всего массива перестановок в памяти.
Поскольку число перестановок является произведением длин каждой из массивы (назовем это numPerms
), вы можете создать функцию getPermutation(n)
, которая возвращает уникальную перестановку между индексами 0
и numPerms - 1
, вычисляя индексы, необходимые для извлечения своих символов, на основе n
.
Как это делается? Если вы думаете о создании перестановок на массивах, каждый из которых содержит: [0, 1, 2, ... 9], это очень просто ... 245-я перестановка (n = 245) - это «245», скорее интуитивно, или:
arrayHundreds[Math.floor(n / 100) % 10]
+ arrayTens[Math.floor(n / 10) % 10]
+ arrayOnes[Math.floor(n / 1) % 10]
Усложнение в вашей проблеме заключается в том, что размеры массива различаются. Мы можем обойти это, заменив n/100
, n/10
и т. Д. Другими делителями. С этой целью мы можем легко вычислить массив делителей. В приведенном выше примере делитель 100 был равен arrayTens.length * arrayOnes.length
. Поэтому мы можем вычислить делитель для данного массива как произведение длин оставшихся массивов. У самого последнего массива всегда есть делитель 1. Кроме того, вместо modding на 10 мы модифицируем длину текущего массива.
Пример кода ниже:
var allArrays = [first, second, third, ...];
// Pre-calculate divisors
var divisors = [];
for (var i = allArrays.length - 1; i >= 0; i--) {
divisors[i] = divisors[i + 1] ? divisors[i + 1] * allArrays[i + 1].length : 1;
}
function getPermutation(n) {
var result = "", curArray;
for (var i = 0; i < allArrays.length; i++) {
curArray = allArrays[i];
result += curArray[Math.floor(n / divisors[i]) % curArray.length];
}
return result;
}
results
должна показывать result
- я замечаю, что вы обратились назад для вычисления делителей, я полагаю, что положение делителя в массиве важно?
– Gary Green
14 April 2011 в 12:05
divisors
будет иметь только один элемент: [1]
, и поэтому он всегда будет делиться на 1, а mod - на длину массива - фактически ничего не делая.
– David Tang
14 April 2011 в 12:57
Предоставленные ответы слишком сложны для меня. Поэтому мое решение:
var allArrays = new Array(['a', 'b'], ['c', 'z'], ['d', 'e', 'f']);
function getPermutation(array, prefix) {
prefix = prefix || '';
if (!array.length) {
return prefix;
}
var result = array[0].reduce(function (result, value) {
return result.concat(getPermutation(array.slice(1), prefix + value));
}, []);
return result;
}
console.log(getPermutation(allArrays));
Вот версия, адаптированная из приведенной выше пары ответов, которая производит результаты в порядке, указанном в OP, и возвращает строки вместо массивов:
function *cartesianProduct(...arrays) {
if (!arrays.length) yield [];
else {
const [tail, ...head] = arrays.reverse();
const beginning = cartesianProduct(...head.reverse());
for (let b of beginning) for (let t of tail) yield b + t;
}
}
const first = ['a', 'b', 'c', 'd'];
const second = ['e'];
const third = ['f', 'g', 'h', 'i', 'j'];
console.log([...cartesianProduct(first, second, third)])
Я предлагаю простую рекурсивную функцию -генератора следующим образом:
// Generate cartesian product of given iterables:
function* cartesian(head, ...tail) {
let remainder = tail.length ? cartesian(...tail) : [[]];
for (let r of remainder) for (let h of head) yield [h, ...r];
}
// Example:
const first = ['a', 'b', 'c', 'd'];
const second = ['e'];
const third = ['f', 'g', 'h', 'i', 'j'];
console.log(...cartesian(first, second, third));
Если вы ищете функцию, совместимую с потоком, которая может обрабатывать двухмерные массивы с любым типом элемента, вы можете использовать функцию ниже.
const getUniqueCombinations = <T>(items : Array<Array<T>>, prepend : Array<T> = []) : Array<Array<T>> => {
if(!items || items.length === 0) return [prepend];
let out = [];
for(let i = 0; i < items[0].length; i++){
out = [...out, ...getUniqueCombinations(items.slice(1), [...prepend, items[0][i]])];
}
return out;
}
Визуализация операции:
in:
[
[Obj1, Obj2, Obj3],
[Obj4, Obj5],
[Obj6, Obj7]
]
out:
[
[Obj1, Obj4, Obj6 ],
[Obj1, Obj4, Obj7 ],
[Obj1, Obj5, Obj6 ],
[Obj1, Obj5, Obj7 ],
[Obj2, Obj4, Obj6 ],
[Obj2, Obj4, Obj7 ],
[Obj2, Obj5, Obj6 ],
[Obj2, Obj5, Obj7 ],
[Obj3, Obj4, Obj6 ],
[Obj3, Obj4, Obj7 ],
[Obj3, Obj5, Obj6 ],
[Obj3, Obj5, Obj7 ]
]
Вы можете использовать типичный обратный трафик:
function cartesianProductConcatenate(arr) {
var data = new Array(arr.length);
return (function* recursive(pos) {
if(pos === arr.length) yield data.join('');
else for(var i=0; i<arr[pos].length; ++i) {
data[pos] = arr[pos][i];
yield* recursive(pos+1);
}
})(0);
}
Я использовал функции генератора, чтобы избежать одновременного выделения всех результатов, но если вы хотите, вы можете
[...cartesianProductConcatenate([['a', 'b'], ['c', 'z'], ['d', 'e', 'f']])];
// ["acd","ace","acf","azd","aze","azf","bcd","bce","bcf","bzd","bze","bzf"]
Вы также можете использовать эту функцию:
const result = (arrayOfArrays) => arrayOfArrays.reduce((t, i) => { let ac = []; for (const ti of t) { for (const ii of i) { ac.push(ti + '/' + ii) } } return ac })
result([['a', 'b', 'c', 'd'], ['e'], ['f', 'g', 'h', 'i', 'j']])
// which will output [ 'a/e/f', 'a/e/g', 'a/e/h','a/e/i','a/e/j','b/e/f','b/e/g','b/e/h','b/e/i','b/e/j','c/e/f','c/e/g','c/e/h','c/e/i','c/e/j','d/e/f','d/e/g','d/e/h','d/e/i','d/e/j']
Конечно, вы можете удалить + '/'
в ac.push(ti + '/' + ii)
, чтобы исключить косую черту из конечного результата. И вы можете заменить те for (... of ...)
на функции forEach (плюс соответствующую точку с запятой до return ac
), независимо от того, с кем вам удобнее.
Копия ответа le_m для непосредственного захвата массивов массивов:
function *combinations(arrOfArr) {
let [head, ...tail] = arrOfArr
let remainder = tail.length ? combinations(tail) : [[]];
for (let r of remainder) for (let h of head) yield [h, ...r];
}
Надеюсь, это сэкономит чье-то время.
var allCasesofRest=
... – Yahel 2 December 2010 в 03:55