How would I complete this problem in less time? - javascript

EDIT: So I changed my code to the following:
function countTinyPairs(a, b, k) {
let pairs = 0;
let arr = [];
b.reverse()
for (num in a) {
result = String(a[num]) + String(b[num])
if (result < k) {
pairs++
}
}
return pairs
}
It works the exact same, without needing to check new arr/push, etc. Would this run in less time? Is there a way to check myself how long it takes?
I was doing a Codesignal javascript practice test (now finished). I had a really hard time, and now know that I need a lot more practice before I can even think of doing the actual test. One of the questions was:
"You are given two arrays of integers a and b of the same length, and an integer k. We will be iterating through array a from left to right, and simultaneously through array b from right to left, and looking at pairs (x, y), where x is from a and y is from b. Such a pair is called tiny if the concatenation xy is strictly less than k."
This was the code that I wrote:
function countTinyPairs(a, b, k) {
let pairs = 0;
let arr = [];
b.reverse()
for (num in a) {
for (num in b) {
result = String(a[num]) + String(b[num])
if (result < k) {
if ((arr.findIndex(e => e === result)) === -1) {
arr.push(String(result));
pairs++
}
}
}
}
return pairs
}
It works, except execution time limit is 4 seconds. There is a hidden test case where my function takes longer than 4 seconds to complete (I'm assuming the arrays have an extreme amount of numbers). I haven't learned anything about the Big O (or whatever it's called) yet, so I have no clue on anything about it.
I'm guessing I'd have to learn about it before I could successfully solve this problem on my own? Or, did I just write bad code and it's possible to do it with better code without knowing a thing about Big O?

First of all, there is no need for multiple loops. You have three:
b.reverse() will reverse b in-place with likely O(n) complexity. Even if it's O(log n) it's still unnecessary.
for (num in a) iterates over a in O(n).
for (num in b) iterates over b in O(n). However, since it's an inner loop, the total is O(n^2).
arr.findIndex(e => e === result) will trigger another O(m) iteration over any pair found. Depending on the value of k that might be only a few times or many. It's already in an O(n^2), so worst case scenario is a high value of k that covers every combination of pairs, so it will be triggered every time and thus you would get a O(n^3) complexity.
Eliminate multiple loops over a and b
Given that both a and b are of equal length, we can trivially iterate over both arrays with a single loop. To achieve reverse iteration, we can employ basic arithmetic to get the index for b that has the same distance from the end as the index of a has from the beginning. Or in other words, you can do this to iterate over both arrays at once in two directions:
const a = [2, 9, 2];
const b = [5, 3, 5];
for (let i = 0; i < a.length; i++) {
const j = b.length - i - 1; //reverse the index for `b`
console.log(`${a[i]}, ${b[j]}`);
}
Note that a.length and b.length are interchangeable, as the problem description says they are identical.
Drop the constant lookups in arr
The next problem is that arr is repeatedly being iterated over only to check the existence of a pair. Instead you can use a Set. Lookups and inserts will have sub-linear complexity by specs. Many implementations can even give you O(1). You can simplify your code to
const pairs = new Set();
/* ... if a pair is found ... */
pairs.add(result);
/* ... produce count ... */
return pairs.size;
Summary
The complete solution can look like this and you only need to iterate once through both a and b at the same time:
function countTinyPairs(a, b, k) {
let pairs = new Set();
for (let i = 0; i < a.length; i++) {
const j = b.length - i - 1;
const pair = `${a[i]}${b[j]}`;
if (Number(pair) < k) {
pairs.add(pair);
}
}
return pairs.size;
}
const a = [2, 9, 2];
const b = [5, 3, 5];
console.log(countTinyPairs(a, b, 30));
Alternative
This can also be expressed using array methods leading to shorter code at the cost of two loops with .map and .filter, then a third to convert to a Set:
function countTinyPairs(a, b, k) {
let pairs = a
.map((x, index) => `${x}${b[b.length - index - 1]}`) //produce pair
.filter(x => Number(x) < k); //leave only tiny ones
return new Set(pairs).size; //deduplicate and count
}
const a = [2, 9, 2];
const b = [5, 3, 5];
console.log(countTinyPairs(a, b, 30));
Using .reduce to bring it down to one loop again:
function countTinyPairs(a, b, k) {
let pairs = a
.reduce((acc, x, index) => {
const pair = `${x}${b[b.length - index - 1]}`;
if (Number(pair) < k) {
return acc.add(pair);
}
return acc;
}, new Set());
return pairs.size; //deduplicate and count
}
const a = [2, 9, 2];
const b = [5, 3, 5];
console.log(countTinyPairs(a, b, 30));
Finally, if you hate yourself you can very make it into a single expression:
const countTinyPairs = (a, b, k) =>
a.reduce(
(acc, x, index) =>
(pair => (Number(pair) < k) ? acc.add(pair) : acc)
(`${x}${b[b.length - index - 1]}`),
new Set()).size;
const a = [2, 9, 2];
const b = [5, 3, 5];
console.log(countTinyPairs(a, b, 30));
No discarding of duplicates
If there is no need to remove duplicates, then the whole code becomes even simpler - you only need to maintain a count, not even collect the pairs:
function countTinyPairs(a, b, k) {
let pairs = 0;
for (let i = 0; i < a.length; i++) {
const j = b.length - i - 1;
const pair = `${a[i]}${b[j]}`;
if (Number(pair) < k) {
pairs++;
}
}
return pairs;
}
const a = [2, 9, 2];
const b = [5, 3, 5];
console.log(countTinyPairs(a, b, 30));
Or using array methods:
.map() + .filter()
function countTinyPairs(a, b, k) {
let pairs = a
.map((x, index) => `${x}${b[b.length - index - 1]}`) //produce pair
.filter(x => Number(x) < k); //leave only tiny ones
return pairs.length;
}
const a = [2, 9, 2];
const b = [5, 3, 5];
console.log(countTinyPairs(a, b, 30));
.reduce()
function countTinyPairs(a, b, k) {
let pairs = a
.reduce((count, x, index) => {
const pair = `${x}${b[b.length - index - 1]}`;
if (Number(pair) < k) {
return count + 1;
}
return count;
}, 0);
return pairs;
}
const a = [2, 9, 2];
const b = [5, 3, 5];
console.log(countTinyPairs(a, b, 30));
Single expression .reduce()
const countTinyPairs = (a, b, k) =>
a.reduce(
(count, x, index) =>
count + (Number(`${x}${b[b.length - index - 1]}`) < k),
0);
const a = [2, 9, 2];
const b = [5, 3, 5];
console.log(countTinyPairs(a, b, 30));

The wording of the question is somewhat ambiguous and it doesn't help that a concrete input and expected output have not been provided. Here's how I would write the solution based on my understanding of the question -
const countTinyPairs = (a, b, k) =>
loop
( ( [ x, xs ] = likeList(a)
, [ y, ys ] = likeList([...b].reverse())
, pairs = 0
) =>
x == null || y == null
? pairs
: recur
( xs
, ys
, Number(`${x}${y}`) < k
? pairs + 1
: pairs
)
)
console.log(countTinyPairs([1,2,3,4,5], [3,4,5,6,7], 40))
// => 3
Using our own generic functions, loop, recur, and likeList, we can dramatically reduce the conceptual overhead required to derive the answer -
const likeList = (t = [], c = 0) =>
({ [Symbol.iterator]: _ => [ t[c], likeList(t, c + 1) ].values() })
const recur = (...v) =>
({ recur, [Symbol.iterator]: _ => v.values() })
const loop = (f, ...init) =>
{ let r = f(...init)
while (r && r.recur === recur)
r = f(...r)
return r
}
If you'd like to learn more about the design choices for these helpers, I encourage you to see this related Q&A.
Expand the snippet below to run the program and verify the results in your own browser -
const likeList = (t = [], c = 0) =>
({ [Symbol.iterator]: _ => [ t[c], likeList(t, c + 1) ].values() })
const recur = (...v) =>
({ recur, [Symbol.iterator]: _ => v.values() })
const loop = (f, ...init) =>
{ let r = f(...init)
while (r && r.recur === recur)
r = f(...r)
return r
}
const countTinyPairs = (a, b, k) =>
loop
( ( [ x, xs ] = likeList(a)
, [ y, ys ] = likeList([...b].reverse())
, pairs = 0
) =>
x == null || y == null
? pairs
: recur
( xs
, ys
, Number(`${x}${y}`) < k
? pairs + 1
: pairs
)
)
console.log(countTinyPairs([1,2,3,4,5], [3,4,5,6,7], 40))
// 3
There's room for an optimisation here. Here we introduce likeReversedList -
const likeReversedList = (t = [], c = 0) =>
({ [Symbol.iterator]: _ => [ t[t.length - c - 1], likeReversedList(t, c + 1) ].values() })
const countTinyPairs = (a, b, k) =>
loop
( ( [ x, xs ] = likeList(a)
, [ y, ys ] = likeList([...b].reverse())
, [ y, ys ] = likeReversedList(b) // <-
, pairs = 0
) =>
// ...
)

The complexity of your code is O(n^2)
Here is how I would solve it. I hope I got the task right, please post some examples of input/output.
If a and b are of equal length you can iterate through them with a single loop. The complexity would be O(n) where n is the length of a.
Why check for duplicates? Is that a requirement?
function test(a,b,k)
{
let x,y,i,xy, result =[];
for (i=0;i<a.length;i++)
{
x = a[i];
y = b[b.length - 1 -i]
xy = parseInt([x,y].join(''));
if (xy < k) result.push(xy);
}
return result;
}
let a = [1,2,3,4,5], b=[4,5,6,7,8], k = 40;
console.log(test(a,b,k));
// Output: [18, 27, 36]

You are given two arrays of integers a and b of the same length. The length is the same so we need to iterate only once improving it from O(n^2) to O(n). You still need to check every element, so that is the best possible complexity for this problem.
The if statement checking for duplicates is as unneeded as the variable pairs.
You can use a Set that will check for duplicates and in the end, return its length instead of counting the pairs manually.
I'm attaching the examplary solution below:
const countTinyPairs = (a, b, k) => {
const set = new Set();
for (let i = 0, j = b.length-1; i < a.length; i++, j--) {
const result = String(a[i]) + String(b[j])
if (result < k) {
set.add(result);
}
}
return set.size;
}
console.log(countTinyPairs([1,2,3,4,5], [1,2,3,4,5], 40))
Edit it is not necessary to have a separate variable called j, but I thought it wad more readable having in stored in a variable.
If we don't need to check for duplicates, then it's enough to write it like this:
const countTinyPairs = (a, b, k) => {
let pairs;
for (let i = 0, j = b.length-1; i < a.length; i++, j--) {
if (String(a[i]) + String(b[j])< k) pairs++
}
return pairs;
}

Related

How to implement a division function without the operator, loop or recursion?

I need to convert this function using the functional programming paradigm but I don't know how, I can use reducer or map creating an array but I don't know how to implement it, i can't use divide operator, loop or recursion;
function divide(dividend, divisor) {
var result = 0;
while (dividend >= divisor) {
dividend -= divisor;
result++;
}
return result;
}
console.log(divide(100, 2));
The way to do it declaratively is with a recursive function....
const divide = (t, b, depth = 0) => t < b ? depth : divide(t-b, b, depth+1);
console.log(`150 / 3 = ${divide(150, 3)}`);
console.log(`24 / 3 = ${divide(24, 3)}`);
console.log(`4 / 3 = ${divide(4, 3)}`);
I'm a bit puzzled by the requirements. My understanding is that loops or recursion aren't prohibited in functional programming. Assuming this is an exercise (it has to be) then here's another way to look at it:
To solve a / b you can count how many b you can fit in a. So for example:
10 / 2 -> [2, 2, 2, 2, 2] -> 5
or:
+2 +2 +2 +2 (map)
10 / 2 -> [2, 4, 6, 8, 10] -> 5
^ ^^
(x) (pred)
So we can unfold the divisor into a list of sums of itself:
const unfold = (pred, map, x) => {
const ys = [];
for (let y = x; pred(y); y = map(y)) ys.push(y);
return ys;
}
unfold(x => x <= 10, x => x + 2, 2);
//=> [2, 4, 6, 8, 10]
Now we can implement divide with unfold and return the length of the list:
const divide = (a, b) =>
unfold(x => x <= a, x => x + b, b)
.length;
divide(10, 2);
//=> 5
My Final solution is this:
const adition = (a, b) => a + b;
const subtraction = (a, b) => a - b;
const multiplication = (a, b) => {
return b >= 0 ? [...Array(b)].reduce((acc) => adition(acc, a), 0) : [...Array(a)].reduce((acc) => adition(acc, b), 0);
};
const division = (a, b) => {
return a === 0 || b === 0 ? 'Error' : b > 1 ? [...Array(a).keys()].reduce((acc, num) => multiplication(num, b) <= a ? adition(acc, 1) : acc, -1) : a;
};

How filtered array if this array was exist on data selected? [duplicate]

Let A and B be two sets. I'm looking for really fast or elegant ways to compute the set difference (A - B or A \B, depending on your preference) between them. The two sets are stored and manipulated as Javascript arrays, as the title says.
Notes:
Gecko-specific tricks are okay
I'd prefer sticking to native functions (but I am open to a lightweight library if it's way faster)
I've seen, but not tested, JS.Set (see previous point)
Edit: I noticed a comment about sets containing duplicate elements. When I say "set" I'm referring to the mathematical definition, which means (among other things) that they do not contain duplicate elements.
I don't know if this is most effective, but perhaps the shortest:
var A = [1, 2, 3, 4];
var B = [1, 3, 4, 7];
var diff = A.filter(function(x) {
return B.indexOf(x) < 0;
});
console.log(diff); // [2]
Updated to ES6:
const A = [1, 2, 3, 4];
const B = [1, 3, 4, 7];
const diff = A.filter(x => !B.includes(x));
console.log(diff); // [2]
Well, 7 years later, with ES6's Set object it's quite easy (but still not as compact as python's A - B), and reportedly faster than indexOf for large arrays:
console.clear();
let a = new Set([1, 2, 3, 4]);
let b = new Set([5, 4, 3, 2]);
let a_minus_b = new Set([...a].filter(x => !b.has(x)));
let b_minus_a = new Set([...b].filter(x => !a.has(x)));
let a_intersect_b = new Set([...a].filter(x => b.has(x)));
let a_union_b = new Set([...a, ...b]);
console.log(...a_minus_b); // {1}
console.log(...b_minus_a); // {5}
console.log(...a_intersect_b); // {2,3,4}
console.log(...a_union_b); // {1,2,3,4,5}
Looking at a lof of these solutions, they do fine for small cases. But, when you blow them up to a million items, the time complexity starts getting silly.
A.filter(v => B.includes(v))
That starts looking like an O(N^2) solution. Since there is an O(N) solution, let's use it, you can easily modify to not be a generator if you're not up to date on your JS runtime.
function *setMinus(A, B) {
const setA = new Set(A);
const setB = new Set(B);
for (const v of setB.values()) {
if (!setA.delete(v)) {
yield v;
}
}
for (const v of setA.values()) {
yield v;
}
}
a = [1,2,3];
b = [2,3,4];
console.log(Array.from(setMinus(a, b)));
While this is a bit more complex than many of the other solutions, when you have large lists this will be far faster.
Let's take a quick look at the performance difference, running it on a set of 1,000,000 random integers between 0...10,000 we see the following performance results.
setMinus time = 181 ms
diff time = 19099 ms
function buildList(count, range) {
result = [];
for (i = 0; i < count; i++) {
result.push(Math.floor(Math.random() * range))
}
return result;
}
function *setMinus(A, B) {
const setA = new Set(A);
const setB = new Set(B);
for (const v of setB.values()) {
if (!setA.delete(v)) {
yield v;
}
}
for (const v of setA.values()) {
yield v;
}
}
function doDiff(A, B) {
return A.filter(function(x) { return B.indexOf(x) < 0 })
}
const listA = buildList(100_000, 100_000_000);
const listB = buildList(100_000, 100_000_000);
let t0 = process.hrtime.bigint()
const _x = Array.from(setMinus(listA, listB))
let t1 = process.hrtime.bigint()
const _y = doDiff(listA, listB)
let t2 = process.hrtime.bigint()
console.log("setMinus time = ", (t1 - t0) / 1_000_000n, "ms");
console.log("diff time = ", (t2 - t1) / 1_000_000n, "ms");
You can use an object as a map to avoid linearly scanning B for each element of A as in user187291's answer:
function setMinus(A, B) {
var map = {}, C = [];
for(var i = B.length; i--; )
map[B[i].toSource()] = null; // any other value would do
for(var i = A.length; i--; ) {
if(!map.hasOwnProperty(A[i].toSource()))
C.push(A[i]);
}
return C;
}
The non-standard toSource() method is used to get unique property names; if all elements already have unique string representations (as is the case with numbers), you can speed up the code by dropping the toSource() invocations.
If you're using Sets, it can be quite simple and performant:
function setDifference(a, b) {
return new Set(Array.from(a).filter(item => !b.has(item)));
}
Since Sets use Hash functions* under the hood, the has function is much faster than indexOf (this matters if you have, say, more than 100 items).
The shortest, using jQuery, is:
var A = [1, 2, 3, 4];
var B = [1, 3, 4, 7];
var diff = $(A).not(B);
console.log(diff.toArray());
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
I would hash the array B, then keep values from the array A not present in B:
function getHash(array){
// Hash an array into a set of properties
//
// params:
// array - (array) (!nil) the array to hash
//
// return: (object)
// hash object with one property set to true for each value in the array
var hash = {};
for (var i=0; i<array.length; i++){
hash[ array[i] ] = true;
}
return hash;
}
function getDifference(a, b){
// compute the difference a\b
//
// params:
// a - (array) (!nil) first array as a set of values (no duplicates)
// b - (array) (!nil) second array as a set of values (no duplicates)
//
// return: (array)
// the set of values (no duplicates) in array a and not in b,
// listed in the same order as in array a.
var hash = getHash(b);
var diff = [];
for (var i=0; i<a.length; i++){
var value = a[i];
if ( !hash[value]){
diff.push(value);
}
}
return diff;
}
Using Underscore.js (Library for functional JS)
>>> var foo = [1,2,3]
>>> var bar = [1,2,4]
>>> _.difference(foo, bar);
[4]
Some simple functions, borrowing from #milan's answer:
const setDifference = (a, b) => new Set([...a].filter(x => !b.has(x)));
const setIntersection = (a, b) => new Set([...a].filter(x => b.has(x)));
const setUnion = (a, b) => new Set([...a, ...b]);
Usage:
const a = new Set([1, 2]);
const b = new Set([2, 3]);
setDifference(a, b); // Set { 1 }
setIntersection(a, b); // Set { 2 }
setUnion(a, b); // Set { 1, 2, 3 }
Incorporating the idea from Christoph and assuming a couple of non-standard iteration methods on arrays and objects/hashes (each and friends), we can get set difference, union and intersection in linear time in about 20 lines total:
var setOPs = {
minusAB : function (a, b) {
var h = {};
b.each(function (v) { h[v] = true; });
return a.filter(function (v) { return !h.hasOwnProperty(v); });
},
unionAB : function (a, b) {
var h = {}, f = function (v) { h[v] = true; };
a.each(f);
b.each(f);
return myUtils.keys(h);
},
intersectAB : function (a, b) {
var h = {};
a.each(function (v) { h[v] = 1; });
b.each(function (v) { h[v] = (h[v] || 0) + 1; });
var fnSel = function (v, count) { return count > 1; };
var fnVal = function (v, c) { return v; };
return myUtils.select(h, fnSel, fnVal);
}
};
This assumes that each and filter are defined for arrays, and that we have two utility methods:
myUtils.keys(hash): returns an
array with the keys of the hash
myUtils.select(hash, fnSelector,
fnEvaluator): returns an array with
the results of calling fnEvaluator
on the key/value pairs for which
fnSelector returns true.
The select() is loosely inspired by Common Lisp, and is merely filter() and map() rolled into one. (It would be better to have them defined on Object.prototype, but doing so wrecks havoc with jQuery, so I settled for static utility methods.)
Performance: Testing with
var a = [], b = [];
for (var i = 100000; i--; ) {
if (i % 2 !== 0) a.push(i);
if (i % 3 !== 0) b.push(i);
}
gives two sets with 50,000 and 66,666 elements. With these values A-B takes about 75ms, while union and intersection are about 150ms each. (Mac Safari 4.0, using Javascript Date for timing.)
I think that's decent payoff for 20 lines of code.
As for the fasted way, this isn't so elegant but I've run some tests to be sure. Loading one array as an object is far faster to process in large quantities:
var t, a, b, c, objA;
// Fill some arrays to compare
a = Array(30000).fill(0).map(function(v,i) {
return i.toFixed();
});
b = Array(20000).fill(0).map(function(v,i) {
return (i*2).toFixed();
});
// Simple indexOf inside filter
t = Date.now();
c = b.filter(function(v) { return a.indexOf(v) < 0; });
console.log('completed indexOf in %j ms with result %j length', Date.now() - t, c.length);
// Load `a` as Object `A` first to avoid indexOf in filter
t = Date.now();
objA = {};
a.forEach(function(v) { objA[v] = true; });
c = b.filter(function(v) { return !objA[v]; });
console.log('completed Object in %j ms with result %j length', Date.now() - t, c.length);
Results:
completed indexOf in 1219 ms with result 5000 length
completed Object in 8 ms with result 5000 length
However, this works with strings only. If you plan to compare numbered sets you'll want to map results with parseFloat.
The function below are ports of the methods found in Python's set() class and follows the TC39 Set methods proposal.
const
union = (a, b) => new Set([...a, ...b]),
intersection = (a, b) => new Set([...a].filter(x => b.has(x))),
difference = (a, b) => new Set([...a].filter(x => !b.has(x))),
symetricDifference = (a, b) => union(difference(a, b), difference(b, a)),
isSubsetOf = (a, b) => [...b].every(x => a.has(x)),
isSupersetOf = (a, b) => [...a].every(x => b.has(x)),
isDisjointFrom = (a, b) => !intersection(a, b).size;
const
a = new Set([1, 2, 3, 4]),
b = new Set([5, 4, 3, 2]);
console.log(...union(a, b)); // [1, 2, 3, 4, 5]
console.log(...intersection(a, b)); // [2, 3, 4]
console.log(...difference(a, b)); // [1]
console.log(...difference(b, a)); // [5]
console.log(...symetricDifference(a, b)); // [1, 5]
const
c = new Set(['A', 'B', 'C', 'D', 'E']),
d = new Set(['B', 'D']);
console.log(isSubsetOf(c, d)); // true
console.log(isSupersetOf(d, c)); // true
const
e = new Set(['A', 'B', 'C']),
f = new Set(['X', 'Y', 'Z']);
console.log(isDisjointFrom(e, f)); // true
.as-console-wrapper { top: 0; max-height: 100% !important; }
This works, but I think another one is much more shorter, and elegant too
A = [1, 'a', 'b', 12];
B = ['a', 3, 4, 'b'];
diff_set = {
ar : {},
diff : Array(),
remove_set : function(a) { ar = a; return this; },
remove: function (el) {
if(ar.indexOf(el)<0) this.diff.push(el);
}
}
A.forEach(diff_set.remove_set(B).remove,diff_set);
C = diff_set.diff;
Using core-js to polyfill the New Set methods proposal:
import "core-js"
new Set(A).difference(B)
In theory, the time complexity should be Θ(n), where n is the number of elements in B.

Best way to get combination of array of elements in Ramda?

What is the best way to implement a function that takes three arguments
smallest length of combinations
highest length of combinations
array of values
and returns all combinations of length l (arg1 <= l <= arg2). E.g.
getComb (2, 2, [1, 2, 3]) === [[1,2], [2,3], [3,1]]
getComb (0, 3, [1, 2, 3]) === [[],[1],[2],[3],[1,2],[2,3],[3,1],[1,2,3]]
(=== is defined here as deep equals without respect to order (almost set equality for both depths of the array) Also duplicate values should be ignored (e.g. getComb(a, b, [x,x,y]) === getComb(a, b, [x,y]) for all a,
b, x, y)
Then a fn to get all combinations can be implemented:
getAllComb = arr => getComb (0, arr.length, arr)
Thanks!
Here's another recursive solution, structured slightly differently from the answer by Nina Scholz. It has a function to choose exactly n elements from the list, and then uses that in the main function, which calls it for each value from min to max:
const choose = (n, xs) =>
n < 1 || n > xs .length
? []
: n == 1
? [...xs .map (x => [x])]
: [
...choose (n - 1, xs .slice (1)) .map (ys => [xs [0], ...ys]),
...choose (n , xs .slice (1))
]
const getCombs = (min, max, xs) =>
xs .length == 0 || min > max
? []
: [...choose (min, xs), ...getCombs (min + 1, max, xs)]
console .log (
getCombs (0, 3, [1, 2, 3]),
getCombs (2, 2, [1, 2, 3])
)
Here getCombs is the main function, and should be fairly clear, just concatenating the result of choose (min, xs) with the result of the recursive call to getCombs (min + 1, max, xs). choose is a nicely reusable function which operates on a double recursion, the first one selecting all those combinations which use the initial element and the second all those that don't.
This doesn't quite match Nina's solution, as it ignores the empty list when min is zero. If you want one that includes the empty list, you could change choose to the (slightly uglier, IMHO) version:
const choose = (n, xs) =>
n < 1 || n > xs .length
? [[]]
: [
...choose (n - 1, xs .slice (1)) .map (ys => [xs [0], ...ys]),
...(n + 1 > xs .length ? [] : choose (n , xs .slice (1)))
]
one way to implement getComb is :
[1,2,3].reduce( (acc, v, i, original) =>
acc.concat(original.slice(i+1).map( w => [w, v] )),
[]);
You could take a recursive approach.
function getComb(min, max, array) {
function iter(left, right = [], push = true) {
if (push && min <= right.length && right.length <= max) result.push(right);
if (!left.length) return;
iter(left.slice(1), [...right, left[0]]);
iter(left.slice(1), right, false);
}
var result = [];
iter(array);
return result;
}
console.log(getComb(2, 2, [1, 2, 3]));
console.log(getComb(0, 3, [1, 2, 3]));
.as-console-wrapper { max-height: 100% !important; top: 0; }
Ok I have a partial solution (for a = 1, b = arr.length):
const list = R.unapply (R.identity)
const xproduct = arr => R.apply (R.liftN (arr.length) (list)) (arr)
const getPerm = arr => xproduct (R.repeat (arr) (arr.length))
const getComb = arr => R.uniq (R.map (R.uniq) (getPerm (arr)))
getComb([1,2,3]) === [[1],[2],[3],[1,2],[2,3],[3,1],[1,2,3]]
There's got to be something better ;)
Here's a solution (atleast to getAllComb) that I'm kinda proud of :) There's a lot of stuff, but most of it is boilerplate
Inspired by bitstrings
// Generic helper functions
const appendIfNotFalse = fn => (acc, val) => R.ifElse (R.equals (false)) (R.always (acc)) (R.flip (R.append) (acc)) (fn (acc, val))
const mapAndFilter = fn => arr => R.reduce (appendIfNotFalse (fn)) ([]) (arr)
// My helper fn
const has1InBitstring = n => idx => (n & 2 ** idx) > 0
// Soltuion
const indices = arr => key => mapAndFilter ((_, j) => has1InBitstring (key) (j) ? j : false) (R.range (0) (arr.length))
const getAllComb = arr => R.times (i => R.props (indices (arr) (i)) (arr)) (2 ** arr.length)
// Example
getAllComb ([1,2,3]) === [[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]

How to find all partitions of a multiset, where each part has distinct elements?

Let's say we have such an array:
myArray = [A, A, B, B, C, C, D, E]
I would like to create an algorithm so that it will find all the combinations that add up to the whole array, where none of the elements are repeated.
Example combinations:
[A, B, C, D, E] [A, B, C]
[A, B, C, D] [A, B, C, E]
[A, B, C] [A, B, C] [D, E]
Clarification: [A, B, C] [A, B, C] [D, E] and [A, B, C] [D, E] [A, B, C] are the same combinations. Also the ordering with the subsets doesn't matter as well. For example [A,B,C] and [B,A,C] should be the same.
So far, I didn't progress beyond
var myArray = ["A", "A", "B", "B", "C", "C", "D", "E"]
console.log([...new Set(myArray)])
But this doesn't help at all, it just returns one distinct set.
I couldn't find a similar problem posted before, so could anyone guide me here in how to achieve this?
I'm getting 315 combinations. Is that right? :)
Here's a recursion:
function distribute(e, i, _comb){
// No more e's
if (e[1] == 0)
return [_comb];
// We're beyond the combination
if (i == -1)
return [_comb.concat([e])];
let result = [];
for (let c=1; c<=Math.min(_comb[i][1], e[1]); c++){
let comb = _comb.map(x => x.slice());
if (c == comb[i][1]){
comb[i][0] += e[0];
} else {
comb[i][1] -= c;
comb.push([comb[i][0] + e[0], c]);
}
result = result.concat(distribute([e[0], e[1] - c], i - 1, comb));
}
let comb = _comb.map(x => x.slice());
return result.concat(distribute(e, i - 1, comb));
}
function f(arr){
function g(i){
if (i == 0)
return [[arr[0]]];
const combs = g(i - 1);
let result = [];
for (let comb of combs)
result = result.concat(
distribute(arr[i], comb.length - 1, comb));
return result;
}
return g(arr.length - 1);
}
function show(arr){
const rs = f(arr);
const set = new Set();
for (let r of rs){
const _r = JSON.stringify(r);
if (set.has(_r))
console.log('Duplicate: ' + _r);
set.add(_r);
}
let str = '';
for (let r of set)
str += '\n' + r
str += '\n\n';
console.log(JSON.stringify(arr));
console.log(set.size + ' combinations:');
console.log(str);
}
show([['A', 2], ['B', 2], ['C', 2], ['D', 1], ['E', 1]]);
You could do it by enumerating all possible combinations, and then finding the permutatinos for every combination, and then filter the element to make sure they are unique. This filtering can be done by inserting into a Set.
The subset function is from #le_m (Check this answer).
function* subsets(array, offset = 0) {
while (offset < array.length) {
let first = array[offset++];
for (let subset of subsets(array, offset)) {
subset.push(first);
yield subset;
}
}
yield [];
}
function* permutations(elements) {
if (elements.length === 1) {
yield elements;
} else {
let [first, ...rest] = elements;
for (let perm of permutations(rest)) {
for (let i = 0; i < elements.length; i++) {
let start = perm.slice(0, i);
let rest = perm.slice(i);
yield [...start, first, ...rest];
}
}
}
}
const arr = ['A', 'a', 'B', 'b', 'C', 'c', 'd', 'e'];
const results = new Set();
for (let subset of subsets(arr)) {
if (subset.length) {
for (let permut of permutations(subset)) {
results.add(permut.join(''));
}
}
}
console.log([...results]);
This generates all possible permutations of the wanted result, so it does not answer the question.
function* combinations(combos) {
yield combos;
for(const [i1, group] of combos.entries()) {
for(const [i2, group2] of combos.slice(i1 + 1).entries()) {
if(group.some(v => group2.includes(v))) continue;
yield* combinations([
...combos.filter((_, index) => index !== i1 && index !== i2),
[...group, ...group2]
]);
}
}
}
console.log([...combinations([1, 2, 3, 4, 1].map(v => ([v])))]);
You could start with an array with combinations that contain only one element, then go over those combinations and merge two arrays of them, proceed recursively.
Playground
You could first group same elements and count them, resulting in a table like this:
1 | 2 | 3 | 4
1 | | 3 | 4
1
(1 is duplicated twice, 3 and 4 once)
Now you could start and take the first four elements out, then the 3 in the second row and then the one in the last row resulting in
[[1, 2, 3, 4], [1, 3, 4], [1]]
Now to get the next combo, just take 3 out from the first row, and let the other values move up:
1 | | 3 | 4
1 | | | 4
now again take out the rows, and you get
[[1, 2, 3], [1, 3, 4], [1, 4]]
Now repeat this pattern with the first row (take 2, 1) and so on, also repeat that with the rows, then you should get the result you want to achieve.
const counts = new Map;
for(const value of [1, 1, 1, 2, 3, 3, 4, 4])
counts.set(value, (counts.get(value) || 0) + 1);
const result = [];
for(let combo = 0; combo < counts.size; combo++) {
const subResult = [];
const combos = [...counts];
for(let i = 0; i <= combo; i++) combos[i][0] -= 1;
subResult.push(combos.slice(0, combo).map(([_, value]) => value);
while(combos.some(([count]) => count > 0)) {
subResult.push(combos.filter(([count]) => count > 0).map(([_, value] => value));
}
result.push(subResult);
}
Implemented an algorithm from scratch that:
gets the frequency of the letters,
starts at the key length and works downwards towards 1
while the frequency map is not empty
add the key to the sub-result
const decrementKey = (o, k) => o[k]--;
const isEmpty = (o) => Object.keys(o).length === 0;
const removeKeys = (o) => Object.keys(o).forEach(k => { if (o[k] < 1) delete o[k]; });
const frequencyMap = (a) => a.reduce((r, v) => Object.assign(r, { [v] : (r[v] || 0) + 1 }), {});
const cloneMap = (o) => Object.keys(o).reduce((r, k) => Object.assign(r, { [k] : o[k] }), {});
let myArray = ["A", "A", "B", "B", "C", "C", "D", "E"];
let groups = solve(myArray);
console.log(groups.map(g => g.map(a => a.join('')).join(' | ')).join('\n'));
function solve(arr) {
let freq = frequencyMap(arr), results = [];
for (let max = Object.keys(freq).length; max > 1; max--) {
let result = [], clone = cloneMap(freq);
while (!isEmpty(clone)) {
result.push(Object.keys(clone).reduce((res, key, index) => {
if (index < max) {
decrementKey(clone, key);
res.push(key);
}
return res;
}, []));
removeKeys(clone);
}
if (result.length > 0) results.push(result);
}
return results;
}
.as-console-wrapper { top: 0; max-height: 100% !important; }

How to sort an array of odd numbers in ascending order, but keep even numbers at their position?

I want to sort only odd numbers without moving even numbers. For example, when I write :
sortArray([5, 3, 2, 8, 1, 4])
The expected result is :
[1, 3, 2, 8, 5, 4]
I am new to JavaScript and I came across a challenge on the Internet that has me perplexed. I normally wouldn't post asking for a solution on the Internet, BUT I have tried for hours and I would like to learn this concept in JavaScript.
The challenge states :
You have an array of numbers.
Your task is to sort ascending odd numbers but even numbers must be on their places.
Zero isn't an odd number and you don't need to move it. If you have an empty array, you need to return it.
Here is my code so far, please take it easy on me I am in the beginning stages of programming.
function sortArray(array) {
let oddNums = [];
for(let i = 0; i < array.length; i++) {
if(array[i] % 2 !== 0) {
oddNums.push(array[i]);
}
}
oddNums = oddNums.sort((a,b)=> a-b);
array.concat(oddNums);
array = array.sort((a,b) => a-b);
return array;
}
You could take a helper array for the odd indices and another for the odd numbers, sort them and apply them back on the previously stored indices of the original array.
var array = [5, 3, 2, 8, 1, 4],
indices = [];
array
.filter((v, i) => v % 2 && indices.push(i))
.sort((a, b) => a - b)
.forEach((v, i) => array[indices[i]] = v);
console.log(array);
Here's a solution using mostly the built-in array methods. Get a list of just the odds, sort it, then map through the original, replacing each item with the first sorted odd if the item is odd, or itself if even:
const array = [5, 3, 2, 8, 1, 4] // to: [1, 3, 2, 8, 5, 4]
function sortOddsOnly(arr) {
const odds = arr
.filter(x => x%2)
.sort((a, b) => a - b);
return arr
.map(x => x%2 ? odds.shift() : x);
}
console.log(sortOddsOnly(array));
I have a solution like this.
Build a sorted odd number array 1st, and then fill the rest of even numbers in order:
const arr = [5, 3, 2, 8, 1, 4];
const odd = arr.filter(i => i%2 !== 0).sort();
let i = 0,
result = [];
arr.forEach(e => {
if (e%2 === 0) {
result.push(e)
} else {
result.push(odd[i]);
i++;
}
});
console.log(result);
just do:
arr.sort((a, b) => a%2 && b%2 ? a - b : 0)
If that works depends on the sort algorithm your browser uses.
A browserindependent version:
for(const [i1, v1] of arr.entries())
for(const [i2, v2] of arr.entries())
if( v1%2 && v2%2 && (i1 < i2) === (v1 > v2))
([arr[i1], arr[i2]] = [v2, v1]);
One of the possible solutions is this. What I have done is created new array odd(array with odd position in original array using Array.prototype.filter) and then sort that array using Array.prototype.sort. Then using Array.prototype.map change value of all odd element of original array with odd array.
x1=[5, 3, 2, 8, 1, 4];
function sortArray(array) {
var odd = array.filter((x,i) => (i+1) % 2 ).sort((a,b) => a > b); //sort odd position and store that in new array
return array.map((x,i) => (i+1) % 2 ? odd.shift() : x ); //if i is odd then replace it with element from
//odd array otherwise keep the element as it is
}
console.log(sortArray(x1));
Here is a possible solution using a slightly customized selection sort :
var xs = [5, 3, 2, 8, 1, 4];
console.log(sortOddsOnly(xs));
function sortOddsOnly (xs) {
var n = xs.length;
for (var i = 0; i < n - 1; i++) {
if (xs[i] % 2 === 1) {
for (var j = i + 1; j < n; j++) {
if (xs[j] % 2 === 1) {
if (xs[i] > xs[j]) {
var min = xs[j];
xs[j] = xs[i];
xs[i] = min;
}
}
}
}
}
return xs;
}
The first two if guarantee that we swap only odd numbers (x % 2 === 1 means "x is odd").
def sort_array(source_array):
b = sorted([n for n in source_array if n % 2 != 0])
c = -1
d = []
for i in source_array:
c = c+1
if i % 2 != 0 :
d.append(c)
for x in range (len(d)):
z = d[x]
source_array[z] = b[x]
return source_array

Categories

Resources