Related
I've written a function that finds all sets of two numbers that sum a target value, given a range of numbers from 0 to x. I'm trying to rewrite it in a way so that you can get a result of a given length n (not only 2), but n numbers that will equal a target when added together.
For example, if the range is 1 to 5 and you want to find all sets of three numbers that add up to 7, the answer would be:
[[1,1,5], [1,2,4], [1,3,3], [2,2,3]]
It looks like the solution is to use a recursive function, but I can't quite figure out how to do this. I've looked at several subset-sum examples on StackOverflow, but none seem to match this particular scenario.
This is not a homework problem. Any help would be appreciated. Here is my findPairs function:
function findPairs(arr, target) {
var pairs = [];
var first = 0;
var last = arr.length-1;
while (first <= last) {
var sum = arr[first] + arr[last];
if (sum === target) {
pairs.push([arr[first], arr[last]]);
first ++;
last--;
}
else if (sum < target) {
first++;
}
else {
last--;
}
}
return pairs;
}
var sample = _.range(11);
console.log(JSON.stringify(findPairs(sample,12)));
// Returns
// [[2,10],[3,9],[4,8],[5,7],[6,6]]
This example uses the lodash _.range function. Fiddle here: https://jsfiddle.net/tbarmann/muoms1vL/10/
You could indeed use recursion. Define the third argument as the length of the sub-arrays you are looking for, and define a recursion function inside that function as follows:
function findSums(arr, target, count) {
var result = [];
function recurse(start, leftOver, selection) {
if (leftOver < 0) return; // failure
if (leftOver === 0 && selection.length == count) {
result.push(selection); // add solution
return;
}
for (var i = start; i < arr.length; i++) {
recurse(i, leftOver-arr[i], selection.concat(arr[i]));
}
}
recurse(0, target, []);
return result;
}
// Demo
var result = findSums([1,2,3,4,5], 7, 3);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Remarks
The solution does not require that the input array has consecutive numbers (range): you might pass it [3,6,4,2,1]. The sub-arrays that are returned will keep the selected elements in order, for example: [3,3,3], [3,4,2] and [6,2,1] could be solutions for targetting 9 with 3 values for that example input.
The numbers must all be non-negative. If negative values are to be allowed, then the optimisation if (leftOver < 0) return; must be removed from the code.
Well, what you are probably looking for is Dynamic Programming. It is an approach where you define mathematically your problem and a recursive solution. Then you try to find a solution, using memoization.
I would make a helper function, where the arguments are (range, target, lengthOfResult). Then do something like:
func helper(arr, target, lengthOfResult){
if(taget == 0) continue;
for(int i=1; i<arr.length-1; i++){
if(taget - arr[i] < 0) return [];
var sub_results = helper(arr, taget - arr[i], lengthOfResult - 1)
foreach(var res in sub_results){
result.concat([arr[i]] + res);
}
}
return result;
}
So you are changing the question for the helper function to "Give me all lists of length-1 which sums up to taget-arr[i]", append to that arr[i]. Then use that to construct the result, for each arr[i].
Basically, every iteration the length will decrease, so the function will terminate at some point. You subtract from the taget whatever number you have now. So you're end result will add up to the desired target.
Note that this algorithm works only with positive numbers. If you can allow negatives, you should remove the if inside the first for-loop.
About the memoization, if you want to allow using each number only once, you could get away with a single array. If you allow reoccurring numbers in the result (like in your example), you probably need a grid; horizontally the target, vertically the lengthOfResult. Then in the beginning of each invocation of the helper method, you check if you already calculated that value. This will save you some recursive calls and make the algorithm not exponential.
function sumPermutations(target, range, number) {
var result = [];
function combo(left, group, sum) {
if(sum > target) return null;
if (left == 0) {
if(sum == target) return group;
return null;
}
for (var i = range.min; i <= range.max; i++) {
var r = combo(left - 1, group.concat(i), sum + i);
if (r)
result.push(r);
}
}
combo(number, [], 0);
return result;
}
console.log(sumPermutations(7, {min: 1, max: 5}, 3));
Note: This gives results with duplicates (all permutaions including those with different orders). You can remove duplicates by sorting the arrays and join thier items and hash them into a hash object.
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.
I'm trying to get the function below to return the average of all elements in array1, but I keep getting null as the result. I can't seem to figure out why.
var array1 = [46,73,-18,0,-442,779,5,1400];
var arrayAverage = function(arrayavg) {
for (var average = 0,answer=0, arrayavg = arrayavg.length;array1 > answer;answer++)
average +=parseInt(arrayavg[answer]);
var calc = average/arrayavg.length;
return calc
};
There are a number of errors, I don't have time to point them all out, hopefully the following is sufficient:
var array1 = [46,73,-18,0,-442,779,5,1400];
var arrayAverage = function(arrayavg) {
I don't know why you using a function expression rather than a function declaration. It doesn't affect the issue, but is more code to write. It's also good to give variables names that express what they are for, so given that the function expects an array:
function arrayAverage(array) {
then:
for (var average = 0,answer=0, arrayavg = arrayavg.length;array1 > answer;answer++)
It's not a good idea to pile all those variable declarations into the for condition, far better to separate concerns and only create variables that you need:
var total = 0;
Now iterate over the array to get the total value. The '{' brackets can be omitted, but it's clearer to include them:
for (var i=0, iLen=array.length; i<iLen; i++) {
total += array[i];
}
Now calculate the average and return it in one statement:
return total/iLen;
}
console.log(arrayAverage(array1)); // 230.375
You need to put brackets after your for loop
I was too fast to answer.
You are re-assigning the passed array to the length of the passed array.
arrayavg = arrayavg.length
this breaks everything.
in the for loop you have assigned arrayavg=arrayavg.length and in the body ,you are accessing average+=arrayavg[answer]. arrayavg is now a primitive type . it will return undefined.
And your loop condition is array1 > answer array1 is an array .you cant compare it like that.it will return false.
modified code.
var array1 = [46,73,-18,0,-442,779,5,1400];
var arrayAverage = function(arrayavg) {
var sum=0;
for (var i=0;i<arrayavg.length;i++)
sum +=parseInt(arrayavg[i]);
return sum/arrayavg.length;
};
You are comparing a number to your array in your for loop. You want to stop the for when answer is the same as array1 length.
Also, don't change your parameter array to its length if you want to get its values in the loop.
var array1 = [46,73,-18,0,-442,779,5,1400];
var arrayAverage = function(arrayavg) {
for (var average = 0,answer=0, len = arrayavg.length;len > answer;answer++)
average +=parseInt(arrayavg[answer]);
var calc = average/len;
return calc
};
And to call it:
arrayAverage(array1);
Your code has two problems in the for loop.
for (var average = 0,answer=0, arrayavg = arrayavg.length;array1 > answer;answer++)
^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^
First thing is you set arrayavg to arrayavg's length BUT in the next line you try to read the index of the array. Well you overwrote the array with a number! Not going to happen.
Second issue you are comparing an array 'array1' to a number 'answer' . What does that check do? Not what you think it is going. You want to be checking the length, but wouldn't you want to be checking the passed in array, not the hardcoded one?
I think the other answers (particularly RobG) have covered most of it. It might help to follow a couple of standard rules (that I use) for your loops:
1) Always have the index as the first declared element, the length of the array (for caching purposes) as the second, and any other variables after them.
2) Always use brackets to separate your loop code from the code in the rest of the function. That way you know when to return your averaged product (ie after the }).
So this is my slightly rewritten code of your problem:
for (var index = 0, len = arrayavg.length, avg = 0; index < len; index++) {
avg += parseInt(arrayavg[index], 10) / len;
}
return avg;
Note also that parseInt should contain a radix (in this case 10). You can leave it out but it's good practice to always include it.
By the way, here's an alternative to your function you might find useful that uses a functional approach using reduce:
var arrayAverage = function (arr) {
return arr.reduce(function (a, b) { return a + b; }) / arr.length;
}
I have an array of objects. I am trying to find duplicated objects and then remove both instances of that object.
Right now I am using this method:
function checkForDups(data){
for(var i = 0; i < data.length; i++){
for(var j = i+1; j < data.length; j++){
if(data[j].number === data[i].number){
data.splice(j,1);
data.splice(i,1);
}
}
}
return data;
}
I believe problem is that it only checks for duplicates that have an index that greater than the current position it is checking. This means objects that are "behind" it in the array are not checked for duplication. Currently I run the array through this function a few times to get the desired results. However, this is obviously extremely inefficient. How could I achieve my desired results more efficiently?
I believe problem is that it only checks for duplicates that have an index that greater than the current position it is checking. This means objects that are "behind" it in the array are not checked for duplication.
No, that's a simply optimisation, made possible by the symmetry of the equality relation. By searching ahead and removing all duplicates in front of you, any current item can't be a duplicate of a previous one or it would have been already eliminated.
However, there are some things you did not take care of:
When splicing (removing) an item from the array, all subsequent ones are moved, and the array changes its length. To really examine all items in the array, you need decrease (or: not increase) the counter variable when removing, so that the new item which is now in the same spot as the removed one gets visited (the spot from which you just removed needs to get revisited).
You probably want to break the inner loop after having found a duplicate, otherwise you compare and remove totally different items.
You have not made clear what the algorithm should do when there are more than 2 duplicate items of the same sort in the array. Leave one when their number is odd? Thanks for your comment.
To remove all existing duplicates, you will need to continue the search, but must not remove the ith element immediately or you won't have anything to compare to furtheron - or you might even remove the ith item multiple times (see #2).
So this modification should fit:
function removeAllDups(data) {
// leaves only items in the array that appeared a single time
// removes everything whose .number can be found multiple times
for (var i = 0; i < data.length; i++) {
var found = false,
num = data[i].number;
for (var j = i+1; j < data.length; j++) {
if (data[j].number === num) {
found = true;
data.splice(j--, 1);
}
}
if (found) {
data.splice(i--, 1);
}
}
return data;
}
Here's an alternate implementation. This uses only two passes through the array, and removes all dupes in cases > 2: http://jsfiddle.net/nrabinowitz/1pdr780j/
function removeDupes(arr, test) {
test = test || function(a, b) { return a === b; };
function find(cache, element) {
return cache.some(test.bind(null, element));
}
var seen = [];
var dupes = [];
var len = arr.length;
var x;
var current;
// First pass - find dupes
for (x = 0; x < len; x++) {
current = arr[x];
if (find(seen, current)) {
dupes.push(current);
} else {
seen.push(current);
}
}
// Second pass: remove dupes. Reverse iteration saves headaches here
for (x = len - 1; x >= 0; x--) {
current = arr[x];
if (find(dupes, current)) {
arr.splice(x, 1);
}
}
}
This is an updated version that takes an optional test function to determine equality for dupe purposes; for the OP's case, the call would be
removeDupes(arr, function(a, b) {
return a.number == b.number;
});
Note that this assumes support for ES5 methods - Array#some, Function#bind. If you need to support older browsers, an ES5 shim or the Underscore library would fit the bill.
You could use underscore.js:
function checkForDups(data) {
return (
_.map(
_.filter(
_.pairs(
_.countBy(data)
), function(v) {return v[1] == 1}
), function(v) {return ~~v[0]}
)
)
}
Example
>> console.log(checkForDups([0,1,2,3,0,1,0,4]))
[2, 3, 4]
You can try it here.
I have a JavaScript array like this:
var myData=['237','124','255','124','366','255'];
I need the array elements to be unique and sorted:
myData[0]='124';
myData[1]='237';
myData[2]='255';
myData[3]='366';
Even though the members of array look like integers, they're not integers, since I have already converted each to be string:
var myData[0]=num.toString();
//...and so on.
Is there any way to do all of these tasks in JavaScript?
This is actually very simple. It is much easier to find unique values, if the values are sorted first:
function sort_unique(arr) {
if (arr.length === 0) return arr;
arr = arr.sort(function (a, b) { return a*1 - b*1; });
var ret = [arr[0]];
for (var i = 1; i < arr.length; i++) { //Start loop at 1: arr[0] can never be a duplicate
if (arr[i-1] !== arr[i]) {
ret.push(arr[i]);
}
}
return ret;
}
console.log(sort_unique(['237','124','255','124','366','255']));
//["124", "237", "255", "366"]
You can now achieve the result in just one line of code.
Using new Set to reduce the array to unique set of values.
Apply the sort method after to order the string values.
var myData=['237','124','255','124','366','255']
var uniqueAndSorted = [...new Set(myData)].sort()
UPDATED for newer methods introduced in JavaScript since time of question.
This might be adequate in circumstances where you can't define the function in advance (like in a bookmarklet):
myData.sort().filter(function(el,i,a){return i===a.indexOf(el)})
Here's my (more modern) approach using Array.protoype.reduce():
[2, 1, 2, 3].reduce((a, x) => a.includes(x) ? a : [...a, x], []).sort()
// returns [1, 2, 3]
Edit: More performant version as pointed out in the comments:
arr.sort().filter((x, i, a) => !i || x != a[i-1])
function sort_unique(arr) {
return arr.sort().filter(function(el,i,a) {
return (i==a.indexOf(el));
});
}
How about:
array.sort().filter(function(elem, index, arr) {
return index == arr.length - 1 || arr[index + 1] != elem
})
This is similar to #loostro answer but instead of using indexOf which will reiterate the array for each element to verify that is the first found, it just checks that the next element is different than the current.
Try using an external library like underscore
var f = _.compose(_.uniq, function(array) {
return _.sortBy(array, _.identity);
});
var sortedUnique = f(array);
This relies on _.compose, _.uniq, _.sortBy, _.identity
See live example
What is it doing?
We want a function that takes an array and then returns a sorted array with the non-unique entries removed. This function needs to do two things, sorting and making the array unique.
This is a good job for composition, so we compose the unique & sort function together. _.uniq can just be applied on the array with one argument so it's just passed to _.compose
the _.sortBy function needs a sorting conditional functional. it expects a function that returns a value and the array will be sorted on that value. Since the value that we are ordering it by is the value in the array we can just pass the _.identity function.
We now have a composition of a function that (takes an array and returns a unique array) and a function that (takes an array and returns a sorted array, sorted by their values).
We simply apply the composition on the array and we have our uniquely sorted array.
This function doesn't fail for more than two duplicates values:
function unique(arr) {
var a = [];
var l = arr.length;
for(var i=0; i<l; i++) {
for(var j=i+1; j<l; j++) {
// If a[i] is found later in the array
if (arr[i] === arr[j])
j = ++i;
}
a.push(arr[i]);
}
return a;
};
Here is a simple one liner with O(N), no complicated loops necessary.
> Object.keys(['a', 'b', 'a'].reduce((l, r) => l[r] = l, {})).sort()
[ 'a', 'b' ]
Explanation
Original data set, assume its coming in from an external function
const data = ['a', 'b', 'a']
We want to group all the values onto an object as keys as the method of deduplication. So we use reduce with an object as the default value:
[].reduce(fn, {})
The next step is to create a reduce function which will put the values in the array onto the object. The end result is an object with a unique set of keys.
const reduced = data.reduce((l, r) => l[r] = l, {})
We set l[r] = l because in javascript the value of the assignment expression is returned when an assignment statement is used as an expression. l is the accumulator object and r is the key value. You can also use Object.assign(l, { [r]: (l[r] || 0) + 1 }) or something similar to get the count of each value if that was important to you.
Next we want to get the keys of that object
const keys = Object.keys(reduced)
Then simply use the built-in sort
console.log(keys.sort())
Which is the set of unique values of the original array, sorted
['a', 'b']
The solution in a more elegant way.
var myData=['237','124','255','124','366','255'];
console.log(Array.from(new Set(myData)).sort((a,b) => a - b));
I know the question is very old, but maybe someone will come in handy
A way to use a custom sort function
//func has to return 0 in the case in which they are equal
sort_unique = function(arr,func) {
func = func || function (a, b) {
return a*1 - b*1;
};
arr = arr.sort(func);
var ret = [arr[0]];
for (var i = 1; i < arr.length; i++) {
if (func(arr[i-1],arr[i]) != 0)
ret.push(arr[i]);
}
}
return ret;
}
Example: desc order for an array of objects
MyArray = sort_unique(MyArray , function(a,b){
return b.iterator_internal*1 - a.iterator_internal*1;
});
No redundant "return" array, no ECMA5 built-ins (I'm pretty sure!) and simple to read.
function removeDuplicates(target_array) {
target_array.sort();
var i = 0;
while(i < target_array.length) {
if(target_array[i] === target_array[i+1]) {
target_array.splice(i+1,1);
}
else {
i += 1;
}
}
return target_array;
}
I guess I'll post this answer for some variety. This technique for purging duplicates is something I picked up on for a project in Flash I'm currently working on about a month or so ago.
What you do is make an object and fill it with both a key and a value utilizing each array item. Since duplicate keys are discarded, duplicates are removed.
var nums = [1, 1, 2, 3, 3, 4, 5, 5, 6, 7, 7, 8, 9, 9, 10];
var newNums = purgeArray(nums);
function purgeArray(ar)
{
var obj = {};
var temp = [];
for(var i=0;i<ar.length;i++)
{
obj[ar[i]] = ar[i];
}
for (var item in obj)
{
temp.push(obj[item]);
}
return temp;
}
There's already 5 other answers, so I don't see a need to post a sorting function.
// Another way, that does not rearrange the original Array
// and spends a little less time handling duplicates.
function uniqueSort(arr, sortby){
var A1= arr.slice();
A1= typeof sortby== 'function'? A1.sort(sortby): A1.sort();
var last= A1.shift(), next, A2= [last];
while(A1.length){
next= A1.shift();
while(next=== last) next= A1.shift();
if(next!=undefined){
A2[A2.length]= next;
last= next;
}
}
return A2;
}
var myData= ['237','124','255','124','366','255','100','1000'];
uniqueSort(myData,function(a,b){return a-b})
// the ordinary sort() returns the same array as the number sort here,
// but some strings of digits do not sort so nicely numerical.
function sort() only is only good if your number has same digit, example:
var myData = ["3","11","1","2"]
will return;
var myData = ["1","11","2","3"]
and here improvement for function from mrmonkington
myData.sort().sort(function(a,b){return a - b;}).filter(function(el,i,a){if(i==a.indexOf(el) & el.length>0)return 1;return 0;})
the above function will also delete empty array and you can checkout the demo below
http://jsbin.com/ahojip/2/edit
O[N^2] solutions are bad, especially when the data is already sorted, there is no need to do two nested loops for removing duplicates. One loop and comparing to the previous element will work great.
A simple solution with O[] of sort() would suffice. My solution is:
function sortUnique(arr, compareFunction) {
let sorted = arr.sort(compareFunction);
let result = sorted.filter(compareFunction
? function(val, i, a) { return (i == 0 || compareFunction(a[i-1], val) != 0); }
: function(val, i, a) { return (i == 0 || a[i-1] !== val); }
);
return result;
}
BTW, can do something like this to have Array.sortUnique() method:
Array.prototype.sortUnique = function(compareFunction) {return sortUnique(this, compareFunction); }
Furthermore, sort() could be modified to remove second element if compare() function returns 0 (equal elements), though that code can become messy (need to revise loop boundaries in the flight). Besides, I stay away from making my own sort() functions in interpreted languages, since it will most certainly degrade the performance. So this addition is for the ECMA 2019+ consideration.
The fastest and simpleness way to do this task.
const N = Math.pow(8, 8)
let data = Array.from({length: N}, () => Math.floor(Math.random() * N))
let newData = {}
let len = data.length
// the magic
while (len--) {
newData[data[len]] = true
}
var array = [2,5,4,2,5,9,4,2,6,9,0,5,4,7,8];
var unique_array = [...new Set(array)]; // [ 2, 5, 4, 9, 6, 0, 7, 8 ]
var uniqueWithSorted = unique_array.sort();
console.log(uniqueWithSorted);
output = [ 0, 2, 4, 5, 6, 7, 8, 9 ]
Here, we used only Set for removing duplicity from the array and then used sort for sorting array in ascending order.
I'm afraid you can't combine these functions, ie. you gotta do something like this:-
myData.unique().sort();
Alternatively you can implement a kind of sortedset (as available in other languages) - which carries both the notion of sorting and removing duplicates, as you require.
Hope this helps.
References:-
Array.sort
Array.unique