find nCr combinations for array items - javascript

I have a problem with my recursive function to find nCr objects in Array.
this works only when r=2, means only when going 1 level inside.
it seems my temporary 'a' array gets all messed up when r > 2
// find nCr combinations in Array
// n -> keys.length
// r -> number of combination to extract (cross = r-1 )
console.clear();
var keys = [0,1,2,3,4,5];
// cross = 'r'-1, s = start point, a = array
function recursive(cross, s, a){
for( var i=s; i < keys.length; i++ ){
if( !cross ){
var b = a.slice(0);
b.push( keys[i] );
set.push( b );
}
else{
a.splice(-1, 1);
a.push(keys[i]);
recursive(cross-1, i+1, a);
}
}
}
var set = [];
recursive(1, 0, []);
console.log( set );

I'm not sure what exactly you're trying to do in your else condition: note that a.splice(-1,1) removes the last element of a, but you start with a empty, when there is nothing to be removed. Even if the code works for r=2, this is a sign that you're doing something wrong. (In fact, each time you go one level deeper, a starts with just the same number of elements as it had at the previous level, so you're removing some element you shouldn't be touching.)
Here's a very slight modification of your algorithm that works correctly. I just changed the order of statements inside the else condition.
var keys = [0,1,2,3,4,5];
function recursive(cross, s, a) {
for( var i=s; i < keys.length; i++ ){
if( !cross ){
var b = a.slice(0);
b.push( keys[i] );
set.push( b );
}
else{
a.push(keys[i]);
recursive(cross-1, i+1, a);
a.splice(-1, 1);
}
}
}
var set = [];
recursive(4, 0, []);
console.log( set );
This last part should print all (6 choose 5)=6 combinations of 5 elements out of keys.
The idea is that now in each call of the function, splice only removes the element that was added in that call, rather than something added in some other function call possibly at some other level. This also guarantees that a remains the same at the end as at the beginning. (Everything you add you remove again.)
This is a common pattern you'll see when writing recursive functions: do some modification, call the function recursively, then do some cleanup that reverses the modification.
BTW, slightly cleaner code (without the cross = r-1 obfuscation) is in the first revision of this answer.

Related

Is there a simple way to quickly pass the entire contents of a given array into an `Array.prototype` method variable without using parameters?

context
I created an array docket to keep track of coordinates as the user clicks on a canvas space. During the main program loop the array is to be scanned by a draw function so that selected pixels can be seen. Originally, inside of my event listener, I was using the push( ) method but then I realized I wanted a way to sort of toggle the pixels.
code description
So I added a method poke( ) to Array.prototype, as seen below, which allows me to push the whole docket array into a local array param.array and assign the trigger coordinate to a local variable param.entry. entry is then pushed into array and array is processed by the main poke( ) loop to ensure there are no duplicate values. If a match is found, both elements are annihilated and param.array is returned to the top, ultimately shrinking docket by 1; If no matches are found then no elements are annihilated and param.array is returned to the top, ultimately expanding docket by 1.
main issue: example 1
Anyway, as the method is currently written, it must be called thusly:
docket.poke( docket, e.key ); Note: for simplicity I have used keyboard key values.
Array.prototype.poke = function( a, b ) {
var bool = { }, i = { }, param = { };
param.array = a; param.entry = b;
//
param.array.push( param.entry );
i.len = param.array.length;
i.end = i.len - 1;
//
for ( i.cur = 0; i.cur < i.len; i.cur++ ) {
bool.match = param.array[ i.cur ] == param.array[ i.end ];
bool.nSelf = !( i.cur == i.end );
//
if ( bool.match && bool.nSelf ) {
param.array.splice( i.end, 1 );
param.array.splice( i.cur, 1 );
//
i.end -= 2;
i.len -= 2;
}
}
//
return param.array;
}
This seems a little redundant, but it offers two critical advantages. First to readability and aesthetic. Being able to visibly pass off the contents of docket to a local array for processing and then visibly return the results to the top I think is very helpful to comprehension. Second, both this example and the next use a sort of confusing truth test to filter out false positives on duplicate value detection. This example doesn't have too though. It could easily be rewritten to compare each element in param.array to param.entry using a tight, no nonsense for loop.
main issue: example 2
docket.poke( e.key ); is the less redundant and more desired approach. This is my code.
Array.prototype.poke = function( a ) {
var bool = { }, entry = a, i = { };
//
this.push( entry );
i.len = this.length;
i.end = i.len - 1;
//
for ( i.cur = 0; i.cur < i.len; i.cur++ ) {
bool.match = this[ i.cur ] == this[ i.end ];
bool.nSelf = !( i.cur == i.end );
//
if ( bool.match && bool.nSelf ) {
this.splice( i.end, 1 );
this.splice( i.cur, 1 );
//
i.end -= 2;
i.len -= 2;
}
}
}
As you can see, this eliminates the the redundancy in the call, but it sacrifices some readability of the method and more importantly the opportunity to really slim up the code using the simple comparison I mentioned above.
So now I'm wondering if there is some less than obvious way that I've missed which will allow me to pass the full contents of my array to a local variable without having to first pass them in as a parameter of its own method.
Any ideas?
There is no reason to define the method on the prototype if you are going to pass the array as an argument. A plain function would be just fine for that.
The second version of your code has indeed the advantage that you can apply the method to a given array instead of passing the array to a function.
The code could however be simplified if:
You would only add the element after you have determined it does not yet occur in the array
You would use indexOf:
Array.prototype.toggle = function(value) {
var index = this.indexOf(value);
if (index > -1) {
this.splice(index, 1);
} else {
this.push(value);
}
}
var a = [4,2,5,8];
a.toggle(2);
console.log(a.join());
a.toggle(2);
console.log(a.join());
NB: I personally find the name toggle more telling than poke.
Consider also the power of a Set: it will find an existing member in constant time (while an array implementation needs linear time), and will also be able to remove it in constant time. So if you are open to using something else than an array for this, go for a Set.
Set.prototype.toggle = function(value) {
if (!this.delete(value)) this.add(value);
}
var a = new Set([4,2,5,8]);
a.toggle(2);
console.log([...a].join());
a.toggle(2);
console.log([...a].join());

Two element combinations of an array javascript

If i have an array of letters, for example
['A','C','D','E']
and I wanted to find all the 2 letter combinations of this array, what is the best way of doing this without using 2 for loops. For example:
for (var i=0; i<arr.length;i++) {
for (var j=i+1; j<arr.length;j++) {
console.log(arr[i] + " " + arr[j]);
}
}
The issue with this, is that if the array becomes massive (1000 elements), it usually times out. Is there another way (alternate data structure etc to do this)?
Use the .map()
Something like this
var arr = ['A','C','D','E'],
combinations = arr.map((v,i)=>arr.slice(i+1).map(v2=>v+v2));
console.log(combinations);
Although this code will also iterate twice over the elements.
(it will actually perform worse than your code since map executes a function for each item and it also creates temporary array copies with the slice, so it is here just for an alternate approach, not a more performant.)
Not only two but with any number of elements you might do as follows;
Array.prototype.combinations = function(n){
return this.reduce((p,c,i,a) => p.concat(n > 1 ? a.slice(i+1).combinations(n-1).map(e => [].concat(e,c))
: [[c]]),[]);
};
console.log(JSON.stringify([1,2,3,4,5,6].combinations(2)));
console.log(JSON.stringify([1,2,3,4,5,6].combinations(3)));
Well as per #Lucas Kot-Zaniewski's comments I have refactored my code to use .push() operations in the place of .concat() instructions and also where required a spread operation i did use Array.prototype.push.apply(context,[args]). These two changes made the code to run 2.5 ~ 3 times faster (resulting 3.5-7 msecs vs 9.5-19 msecs) when an input of 100 items array is given and a combination of two of each is requested. Yet once tried with 1000 items of 2 combinations the difference is more dramatic like 400ms vs 6000ms.
A test can be seen at https://repl.it/DyrU
Array.prototype.combinations = function(n){
return this.reduce((p,c,i,a) => (Array.prototype.push.apply(p,n > 1 ? a.slice(i+1).combinations(n-1).map(e => (e.push(c),e))
: [[c]]),p),[]);
};
console.log(JSON.stringify([1,2,3,4,5,6].combinations(2)));
I really beat this one into the ground. As expected, #Louis Durand 's answer of two nested for loops was fastest on an array containing 100 strings (around 4 ms on my machine). This shows that a nested loop is probably your best bet in this situation.
Second fastest is my recursive solution which did the same in around 7-8 ms.
Third was #Redu 's answer which clocked in around 12-15 ms for the same task. I suspect his implementation is slower because he uses slice method in his algo to update array (other answers just increment the index leaving the input array unchanged which is much faster). Also this implementation results in multiple copies of the input array being stored in memory (every time the function is called it creates a new input array from the original array from which it removes the first elelment). This could also potentially affect performance.
So to answer your question: no I don't think there is a much better approach to what you are doing other than concatenating onto string and printing answers at the very end (what Louis suggested).
var arr = [];
for (var i = 0; i< 100; i++){
arr.push(i+"");
}
/*
console.time("test0");
test0();
function test0() {
var s = "";
for (var i=0; i<arr.length-1;i++) {
for (var j=i+1; j<arr.length;j++) {
s += arr[i] + " " + arr[j]+" ; ";
}
s += "\n";
}
console.log(s);
}
console.timeEnd("test0");
*/
console.time("test1");
test1();
function test1() {
var output = [];
getCombos(0, 0, [], 2);
console.log(JSON.stringify(output));
function getCombos(index, depth, tmp, k){
if(depth < k){
for(var i = index; i<arr.length; i++){
var tmp1 = [arr[i]];
Array.prototype.push.apply(tmp1, tmp);
getCombos(i+1, depth+1,tmp1, k);
}
}else{
output.push(tmp);
}
}
}
console.timeEnd("test1");
/*
console.time("test2");
test2();
function test2(){
Array.prototype.combinations = function(n){
return this.reduce((p,c,i,a) => (Array.prototype.push.apply(p,n > 1 ? a.slice(i+1).combinations(n-1).map(e => (e.push(c),e))
: [[c]]),p),[]);
};
console.log(JSON.stringify(arr.combinations(2)));
}
console.timeEnd("test2");*/
Here is a recursive solution that doesn't solve your time complexity problem but is another way to consider. The added advantage is that you can generalize it to any k so you are not stuck finding only combinations of two letters. Also you only declare one loop (although more than one copy of it will exist in your call stack)
var arr = ["a", "b", "c", "d", "e"];
var output = "";
getCombos(0, 0, [], 2);
console.log(output);
function getCombos(index, depth, tmp, k){
if(depth < k){
for(var i = index; i<arr.length; i++){
var tmp1 = [...tmp, arr[i]];
getCombos(i+1, depth+1,tmp1, k);
}
}else{
output += tmp.toString() + ";";
}
}
I think your code has a mistake cause here, E would never be used.
It should rather be:
var s = "";
for (var i=0; i<arr.length-1;i++) {
for (var j=i+1; j<arr.length;j++) {
s += arr[i] + " " + arr[j]+" ; ";
}
s += "\n";
}
console.log(s);
Note that if you log everything in the console, it's no surprise that it times out.

Give structure to 'random' function js?

I have an array and a function that picks randomly elements from this array and displays them in a div.
My array:
var testarray = [A, B, C, D, E, F];
Part of the js function:
var new_word = testarray[Math.floor((Math.random()*testarray.length)+1)];
$("#stimuli").text(new_word);
My question is, is there a way I can have them picked randomly in a certain ratio/order?
For example, that if I have my function executed 12 times, that each of the six letters is displayed exactly twice, and that there can never be the same letter displayed twice in a row?
You might want to try a quasi-random sequence. These sequences have the properties you're after. http://en.wikipedia.org/wiki/Low-discrepancy_sequence
Edit:
To your question in the comment: Of course there are hundreds ways to solve a problem. Think about using artificial intelligence, a mathematical algorithm or the answers given by others here. It depends on what you really want to achieve. I just gave a robust solution that is easy to understand and implement..
Here's another (different approach), same result but with the prevention that values displays twice in a row.
Jsfiddle: http://jsfiddle.net/kychan/jJE7F/
Code:
function StructuredRandom(arr, nDisplay)
{
// storage array.
this.mVar = [];
this.previous;
// add it in the storage.
for (var i in arr)
for (var j=0; j<nDisplay; j++)
this.mVar.push(arr[i]);
// shuffle it, making it 'random'.
for(var a, b, c = this.mVar.length; c; a = Math.floor(Math.random() * c), b = this.mVar[--c], this.mVar[c] = this.mVar[a], this.mVar[a] = b);
// call this when you want the next item.
this.next = function()
{
// default value if empty.
if (this.mVar.length==0) return 0;
// if this is the last element...
if (this.mVar.length==1)
{
// we must give it..
return this.mVar.pop();
// or give a default value,
// because we can't 'control' re-occuring values.
return -1;
}
// fetch next element.
var element = this.mVar.pop();
// check if this was already given before.
if (element==this.previous)
{
// put it on top if so.
this.mVar.unshift(element);
// call the function again for next number.
return this.next();
}
// set 'previous' for next call.
this.previous = element;
// give an element if not.
return element;
};
}
NOTE: In this example we can't fully control that the same values are displayed twice.. This is because we can control the first numbers, but when there is only one number left to display, we must either give it or display a default value for it, thus there is a chance that the same value is shown.
Good luck!
Like this?
var arr = [1,2,3,4,5,6,7], // array with random values.
maxDispl = 2, // max display.
arr2 = init(arr) // storage.
;
// create object of given array.
function init(arr)
{
var pop = [];
for (var i in arr)
{
pop.push({value:arr[i], displayed:0});
}
return pop;
}
// show random number using global var arr2.
function showRandom()
{
// return if all numbers has been given.
if (arr2.length<1) return;
var randIndex= Math.floor(Math.random()*arr2.length);
if (arr2[randIndex].displayed<maxDispl)
{
document.getElementById('show').innerHTML+=arr2[randIndex].value + ', ';
arr2[randIndex].displayed++;
}
else
{
// remove from temp array.
arr2.splice(randIndex, 1);
// search for a new random.
showRandom();
}
}
// iterate the function *maxDispl plus random.
var length = (arr.length*maxDispl) + 2;
for (var i=0; i<length; i++)
{
showRandom();
}
jsfiddle: http://jsfiddle.net/kychan/JfV77/3/

Infinite recursion in JavaScript quicksort?

Here is the quicksort code I wrote. The function doesn't work because it can't reach the base case. If I log the pivot, r and l to the console, they remain the same no matter how many times the sort function is called. So I wonder if the argument l, r are not really passed into the function as data. Why did it happen?
function sort(data){
if(data.length < 2){
return data;
}
else{
var l = [];
var r = [];
var pivot = parseInt(data.length/2);
for(i=0; i<data.length; i++){
if(data[i] > data[pivot]){
r.push(data[i]);
}
else{
l.push(data[i]);
}
}
return sort(l).concat(sort(r));
}
}
I think that the issue here is that your partitioning step does not necessarily shrink the input array. For example, let's trace what happens if you try sorting [1, 2]. In this case, your pivot element will be the element 2. Since 1 > 2 is false, 1 is added to the list l. Since 2 > 2 is false, 2 is added to the list l. As a result, your recursive call on the list l will have exactly the same arguments as your original call, causing infinite recursion.
To fix this, try splitting the input into three lists - one of smaller values, one of equal values, and one of greater values. This code is shown here:
function sort(data){
if (data.length < 2){
return data;
} else {
var l = [];
var r = [];
var e = [];
var i = 0;
var pivot = (data.length / 2) | 0;
for(i = 0; i < data.length; i++) {
if (data[i] > data[pivot]) {
r.push(data[i]);
} else if (data[i] < data[pivot]) {
l.push(data[i]);
} else {
e.push(data[i]);
}
}
return sort(l).concat(e, sort(r));
}
}
This new version explicitly groups the equal elements into their own list, so they aren't recursively sorted by either of the recursive calls. It also gracefully handles duplicate elements.
If you pick the largest value of the array as the pivot element, then all values of data will end up in the array l and none in r. Thus will make the recursion never stop (and keep l, r and pivot at the same values).
Unless this is a brain excercise, using data.sort() should do a better job. ;)
JavaScript passes objects by reference (arrays are objects too). If you want to pass them by value, you need to use the splice function as explained here.
Note that this will create a lot of copies of your data. You probably want to use the native sort() function.

Can I select 2nd element of a 2 dimensional array by value of the first element in Javascript?

I have a JSON response like this:
var errorLog = "[[\"comp\",\"Please add company name!\"],
[\"zip\",\"Please add zip code!\"],
...
Which I'm deserializing like this:
var log = jQuery.parseJSON(errorLog);
Now I can access elements like this:
log[1][1] > "Please add company name"
Question:
If I have the first value comp, is there a way to directly get the 2nd value by doing:
log[comp][1]
without looping through the whole array.
Thanks for help!
No. Unless the 'value' of the first array (maybe I should say, the first dimension, or the first row), is also it's key. That is, unless it is something like this:
log = {
'comp': 'Please add a company name'
.
.
.
}
Now, log['comp'] or log.comp is legal.
There are two was to do this, but neither avoids a loop. The first is to loop through the array each time you access the items:
var val = '';
for (var i = 0; i < errorLog.length; i++) {
if (errorLog[i][0] === "comp") {
val = errorLog[i][1];
break;
}
}
The other would be to work your array into an object and access it with object notation.
var errors = {};
for (var i = 0; i < errorLog.length; i++) {
errors[errorLog[i][0]] = errorLog[i][1];
}
You could then access the relevant value with errors.comp.
If you're only looking once, the first option is probably better. If you may look more than once, it's probably best to use the second system since (a) you only need to do the loop once, which is more efficient, (b) you don't repeat yourself with the looping code, (c) it's immediately obvious what you're trying to do.
No matter what you are going to loop through the array somehow even it is obscured for you a bit by tools like jQuery.
You could create an object from the array as has been suggested like this:
var objLookup = function(arr, search) {
var o = {}, i, l, first, second;
for (i=0, l=arr.length; i<l; i++) {
first = arr[i][0]; // These variables are for convenience and readability.
second = arr[i][1]; // The function could be rewritten without them.
o[first] = second;
}
return o[search];
}
But the faster solution would be to just loop through the array and return the value as soon as it is found:
var indexLookup = function(arr, search){
var index = -1, i, l;
for (i = 0, l = arr.length; i<l; i++) {
if (arr[i][0] === search) return arr[i][1];
}
return undefined;
}
You could then just use these functions like this in your code so that you don't have to have the looping in the middle of all your code:
var log = [
["comp","Please add company name!"],
["zip","Please add zip code!"]
];
objLookup(log, "zip"); // Please add zip code!
indexLookup(log, "comp"); // Please add company name!
Here is a jsfiddle that shows these in use.
Have you looked at jQuery's grep or inArray method?
See this discussion
Are there any jquery features to query multi-dimensional arrays in a similar fashion to the DOM?

Categories