In a recursive function, where to store results? - javascript

I'm currently trying to let JavaScript generate a truth table for a boolean function. Given a function, the code should just list all boolean combinations possible, with the function output from each combination.
As for generating all combinations, I put this together (simplified to the combinations code only):
var table = [];
function combinations(current) {
var current = current || [];
if(current.length === 3) {
table.push(current);
} else {
var c = copy(current);
c.push(true);
combinations(c);
c = copy(current);
c.push(false);
combinations(c);
}
}
function copy(a) {
var r = [];
for(var i = 0; i < a.length; i++) r.push(a[i]);
return r;
}
combinations(); // now table consists of each pair of 3 boolean values
So basically when it has reached a combination (i.e. current.length === 3), it pushes a result record to table. I was wondering, however, whether this is the recommended way of storing results of a recursive function.
I faced the recommendation of using return inside the recursive function, but how would one implement such a thing - i.e., if combinations in the end has to return an array containing all elements, how is it possible to do so? I could, of course, just use return table in the end, but I'm actually looking for a way to do this all inside the function, without an external variable like now.
So, how do I make combinations return the results as an array without using an external variable?

Use Array.concat().
function combinations(current) {
var current = current || [];
if(current.length === 3) {
return [current];
} else {
return combinations(current.concat(true)).concat(combinations(current.concat(false)));
}
}
var table = combinations(); // now table consists of each pair of 3 boolean values
console.log(table);
Much more elegant, no?
Demo →

To avoid polluting the global space you could use a closure to contain your recursive function. There is an excellent writeup of this concept at http://drewwells.net/blog/2010/recursion-in-javascript/.

Your current solution seems fine to me. It might not be the most elegant, but it is simple and does the job (the only ugly bit is the hardcoded 3 - you should turn that into a parameter)
Your real question seems to be more language-agnostic than Javascript. If you want the function to return the combinations, than you can prefectly do so, just clearly have in mind what your function should return and write the base and recursive cases:
function combinations(domain, n){
//returns a list of combinations of length `n` with elements from `domain`
if(n <= 0){
return [[]]; //the empty combination is the only solution
}else{
var small_combs = combinations(domain, n-1);
var big_combs = [];
for(var i=0; i<domain.length; i++){
for(var j=0; j<small_combs.length; j++){
big_combs.push(small_combs[j].concat(domain[i]))
}
}
return big_combs;
}
}
table = combinations([true, false], 3);

var id = { "object": "page", "entry": [{ "id": "1588811284674233", "time": 1511177084837, "messaging": [{ "sender": { "id": "1393377930761248" }, "recipient": { "id": "1588811284674233" }, "timestamp": 1511177084553, "message": { "mid": "mid.$cAAX_9pLcfu1mCnGmiVf2Sxd2erI2", "seq": 1882, "text": "a" } }] }] };
function getKey(obj, data) {
var data = data || [];
if (obj) {
var keys = Object.keys(obj);
for (var pos in keys) {
console.log();
data.push(keys[pos]);
if ((obj[keys[pos]].constructor === Array)) {
for (var i = 0; i < obj[keys[pos]].length; i++) {
getKey(obj[keys[pos]][i], data);
}
}
else if (obj[keys[pos]].constructor === Object) {
getKey(obj[keys[pos]], data);
}
}
return data;
}
}

Related

I need to write code that will add non-repeating elements to a new array from an old one

I need to write code that will add non-repeating elements to a new array from an old one.
I made 2 functions similar to .includes and .push., .includes checks if the new array has the same element as the old one, if not, it returns true.
Then I turn to the .push function and add this element to the new array. The problem is that in the arrIncludesTest function loop, i is reset every time it is called and the function returns true all the time. Ready-made methods do not suit me, I have to write them myself.
function unique(arr) {
let result = [];
for (let elem of arr) {
if (arrIncludesTest(result, elem)) {
arrPush1(result, elem);
}
}
return result;
}
function arrIncludesTest(result, elem) {
for (let i = 0; i <= result.length; i++) {
if (result[i] !== elem) {
return true;
}
}
return false;
}
function arrPush1(result, elem){
result[result.length] = elem
}
console.log(unique([1,2,2,3,4,4,4,44]))
Your for loop should be using < instead of <=, otherwise i will equal the length of your array which is an invalid index. Your if-statement within arrIncludesTest should only return true when the current element equals the search elem, so you need to change it to use == (or ===), not !==. By using !== you'll almost immediately stop your loop as you'll return false because the first element of result doesn't equal your elem. Then you can push your element into your array if the current element isn't in your array yet !arrIncludesTest(result, elem).
See example below:
function unique(arr) {
let result = [];
for (let elem of arr) {
if (!arrIncludesTest(result, elem)) {
arrPush1(result, elem);
}
}
return result;
}
function arrIncludesTest(result, elem) {
for (let i = 0; i < result.length; i++) {
if (result[i] === elem) {
return true;
}
}
return false;
}
function arrPush1(result, elem) {
result[result.length] = elem;
}
console.log(unique([1, 2, 2, 3, 4, 4, 4, 44]));
Your restrictions about what you can and can't use is a little vague, but I doubt that you can use a Set. As an alternative, another option could be to create an object. Object can only store unique keys, so adding the same key twice doesn't double it up. Within the object, you can add your elements as keys to the object, and then loop over the keys to get unique values:
function unique(arr) {
const obj = {};
for(const elem of arr)
obj[elem] = true;
const res = [];
for(const key in obj)
res[res.length] = +key; // convert the key to a number with the unary plus operator
return res;
}
console.log(unique([1, 2, 2, 3, 4, 4, 4, 44]))
Although, if you're going to do something like this, a Set is more appropriate (as above we're storing an object with meaningless values)

Why is my code pushing every permutation twice?

I'm confused as to why my code is pushing every permutation twice. Please someone help. I'm using heap's algorithm:
var regex = /(.)\1+/g;
function permAlone(str) {
var newArray = str.split('');
var n = newArray.length;
var permutations = [];
var tmp;
function swap(index1, index2) {
tmp = newArray[index1];
newArray[index1] = newArray[index2];
newArray[index2] = tmp;
}
function generate(n, newArray) {
if (n === 1) {
permutations.push(newArray.join(''));
} else {
for(var i = 0; i<n-1; i++) {
generate(n-1, newArray);
swap(n % 2 ? 0 : i, n-1);
permutations.push(newArray.join(''));
}
generate(n-1, newArray);
}
}
generate(n, newArray);
return permutations;
}
permAlone('aab');
The array that is returned is:
["aab", "aab", "aab", "baa", "baa", "aba", "aba", "aba", "baa", "baa"]
So as you can see, the permutations are appearing many more times than intended for each thing. Any help would be great
The code's a little complex and it's difficult to track given the recursion, but if all you want is an array with only unique values, you can simply apply the following code to the result array:
function stripDuplicates(input) {
if (!input || typeof(input) !== 'object' || !('length' in input)) {
throw new Error('input argument is not array.');
}
var newArray = [];
for (var i = 0; i < input.length; i++) {
if (newArray.indexOf(input[i]) === -1) {
newArray.push(input[i]);
}
}
return newArray;
}
This could also be done functionally rather than imperatively, but that's really more of a preference than an optimization issue.
Bálint also points out that you could merely convert the result to a Set, then convert the Set back to an Array, which would automatically strip out any duplicates. Beware, though, that Set is a comparatively new affordance in Javascript and will not function in pre-ES6 environments.
You have a call to:
permutations.push(newArray.join(''));
inside of your for loop. That shouldn't be there. And then, of course if you are permuting strings that have duplicate characters, well, expect to see dupes. e.g., if you permute the string "aa" you'll get two entries from this algorithm "aa" and "aa". Heap's algorithm doesn't try to remove dupes, it treats each element as unique within the string. Obviously, it's trivial to use remove dupes if that's something you care about doing.

javascript: adding strings into an object through a class method

I'm trying to create a javascript class with a couple of methods that would let me add view and delete. I'm stuck with the add method, it should return me a unique ID of each string passed into the add method, but instead I can't figure out how to jump to a next code block when I'm done adding strings. Below is some of my current code:
var obj = {};
var arr = [];
var Words = function(){
this.add = function(newWord){
if(newWord !== false){
arr.push(newWord);
} else {
for (var i = 0; i < arr.length; i++){
obj[i] = arr[i];
return obj[i];
}
}
var words = new Words();
words.add('first');
words.add('second');
words.add('third');
I feel I should be creating a next() function of some sort that runs after I'm done pushing the last passed string, but I seem to have forgotten everything.
While I don't really know what you're trying to accomplish, there are some problems with your code...
An Object has keys and values. For every key there is a value.
Ex. mykey: "corresponding value"
for (var i = 0; i < arr.length; i++){
obj[i] = arr[i];
return obj[i];
}
You are setting obj[i] when i is an integer, so unless your keys are integers (probably not), you need to specify keys.
Otherwise, use another array for what you are doing (if possible).
If you are actually filling this object with keys that are numerals, then ignore the above.
I would also point you to using callbacks for finding out when .add() is finished (or just run your code after running .add() ??).
Why not just create an array, set it's values as you want, and retrieve them based on iteration?
var array = [];
//add
array.push("first");
//view value
console.log(array[0]); // output: "first"
//view index
array.indexOf("first"); // output 0
//delete
array.splice(0, 1);
Then wrap this as a class if you'd like:
var array = [];
var Words = function() {
this.add = function(input) {
array.push(input);
}
this.view = function(input) {
return array[input];
}
this.delete = function(input) {
array.splice(input, 1);
}
}
Hope this helps.

Javascript number of object by different property values in array

Best way to count the number of objects in a array with different object property p values.
function([{"p":"a"},{"p":"b"},{"p":"a"}]){
// some code
}
// in this case return 2
You can use Array.prototype.filter() to keep values that you want. In this case, you must create a variable temp for storing duplicate values and also within the filter function returns true if it does not exist in case. So you have a new array of unique values.
var arr = [{"p":"a"},{"p":"b"},{"p":"a"}], temp = [];
var arrUniques = arr.filter(function(obj){
return temp.indexOf(obj.p) === -1 ? !!temp.push(obj.p) : false
});
alert(arrUniques.length)
With a Map:
var props = new Map();
for (var i = 0; i < array.length; i++) {
var prop = array[i].p,
count = props.get(prop) || 0;
props.set(prop, count + 1);
}
var size = props.size;
If your properties can be safely casted to strings, you can use a common object:
var props = {};
...
var size = Object.keys(props).length;
Otherwise, Map is your answer.
function getUniquePropertyCount(a, p) {
return a.reduce(function (res, el) {
!~res.indexOf(el[p]) && res.push(el[p]);
return res;
}, []).length;
}
document.write(getUniquePropertyCount([{ "p": "a" }, { "p": "b" }, { "p": "a" }], 'p'));
I suppose this is one of those questions where if you ask four programmers, you'll get five answers.
The other answers so far show some interesting approaches. I would watch out for browser compatibility; for example Map() is only available in the newest browsers, not in IE 10 or prior.
So here's one more solution. It's a bit more code, but it's pretty easy to understand, and it works in every browser:
function countUniqueProperties( key, array ) {
var count = 0, values = {};
for( var i = 0; i < array.length; ++i ) {
var value = array[i][key];
if( ! Object.prototype.hasOwnProperty.call( values, value) ) {
++count;
values[value] = true;
}
}
return count;
}
countUniqueProperties( 'p', [ {p:'a'}, {p:'b'}, {p:'a'} ] );
The one complicated part here is the Object.prototype.hasOwnProperty.call(values,value). You could just use values.hasOwnProperty(value), but that would fail if one of your property values was the string "hasOwnProperty":
countUniqueProperties( 'p', [ {p:'a'}, {p:'hasOwnProperty'}, {p:'a'} ] );
Using the longer form avoids this issue.
lodash is nice for things like this.
Use
_.uniq([{"p":"a"},{"p":"b"},{"p":"a"}]).length
and it'll return 2.

This simple comparison of jQuery objects does not compare

Curious what I'm doing wrong here :
employee_ids = $('[data-employee_id="'+employee+'"]');
timestamp_ids = $('[data-scheduled_on="'+timestamp+'"]');
var common = $.grep(timestamp_ids, function(element) {
$.each(employee_ids, function(idx, item) {
if ( item === element ) { console.log ("omg!") };
});
});
This returns just the list of timestamp_ids and not that array compared against employee_ids looking for a single match.
You are not using .grep correctly. Each iteration of grep should return a boolean: true to add it to the result array, false to ignore it.
var listA = [1, 2, 3];
var listB = [2, 3, 4];
var union = $.grep(listA, function (element) {
return listB.indexOf(element) !== -1;
});
Note that IE does not support .indexOf on Arrays, you will have to implement the comparison some other way.
EDIT: if you are trying to find a single item of an array that matches some criteria, i would suggest just using a regular for loop:
var result;
for (var i = 0; i < yourArray.length; i++) {
if (yourArray[i].id === employee_ID) { // whatever conditions you have
result = yourArray[i];
break;
}
}
if (result) {
// do whatever
} else {
// no match
}
Whatever else is wrong with that, it looks like the error is happening at $.grep
What is the typeof of timestamp_ids? According to the jQ docs, it needs to be an array.
Will this work?
employee_ids = $('[data-employee_id="'+employee+'"]');
timestamp_ids = $('[data-scheduled_on="'+timestamp+'"]');
var common = $.grep(timestamp_ids, function(element) {
return !($.inArray(element, timestamp_ids) == -1)
});
Whoa! Thanks for everyone's help. I just realized I could do this :
$('[data-employee_id="'+employee+'"][data-scheduled_on="'+timestamp+'"]');
I also realized I'm an idiot :(

Categories

Resources