Javascript: union of multiple arguments - javascript

I want to write a function that will return a union of the array arguments passed.
I don't want to use a decrementing while statement nor the underscore _.union function.
union([1,4,78,98],[45,56,3],[2,3,45]);
returns --> [1,4,78,98,45,56,2]
This is what I have so far and it is not correct:
union: function(){
var i, j, k;
var newArray = [];
for(i = 0; i < arguments.length; i++){
j = arguments[i];
l = k[arguments[i]];
for (){
j = k[i];
newArray.push(j);
}
}
return newArray;
}
I was hoping I can arrive at a solution with multiple for loops.

The idea is to flatten then remove duplicates. In a functional style you could do something like this:
function union() {
var arrs = [].concat.apply([], arguments);
return arrs.filter(function(v, i) {
return arrs.indexOf(v) == i;
});
}
That should give you the same result as Underscore's union.
Demo: http://jsbin.com/azulok/1/edit

You can use .reduce().
union([1,4,78,98],[45,56,3],[2,3,45]);
function union() {
return [].reduce.call(arguments, function(res, arr) {
return res.concat(arr.filter(function(item) {
return res.indexOf(item) === -1;
}))
})
}
Break it up a little if you prefer:
function union() {
return [].reduce.call(arguments, function(res, arr) {
return res.concat(arr.filter(notIn(res)))
})
}
function notIn(arr) {
return function(item) {
return arr.indexOf(item) === -1;
}
}
And of course you can reuse the .reduce() callback as well, so that if you actually have an Array of Arrays, you can do this:
var res = arrays.reduce(joinUnique);
function joinUnique(res, arr) {
return res.concat(arr.filter(notIn(res)))
}
function notIn(arr) {
return function(item) {
return arr.indexOf(item) === -1;
}
}

The simplest way of doing it is to use Set.
const data = new Set([...[1,4,78,98], ...[45,56,3], ...[2,3,45]]);
data.forEach(i => console.log(i));

Related

JavaScript changing object list to array. return undefined

function listToArray(list){
var newArray = [];
repeat();
function repeat(){
newArray.push(list.value);
if(list.rest == null){
return obj = newArray; // I don't know why It returns undefined here
}
else {
list = list.rest;
repeat();
}
}
}
// and this will return an array
function reTurn(){
var listVar = [5, 4, 3, 2, 1];
return obj = listVar;
}
This function does change my obj when I look for it in console, but returns undefined.
Anyone can help me? please
just return newArray - you can't return an assignment like that.
You're returning from the inner function, not the outer one. Add a return newArray to the bottom of listToArray. Change the return statement in repeat to just return;.
function listToArray(list){
var newArray = [];
repeat();
function repeat(){
newArray.push(list.value);
if (list.rest) {
list = list.rest;
repeat();
}
}
return newArray;
}
There's nothing really wrong with the recursive approach, but it's not necessary. You could just as easily write
function listToArray(list){
var newArray = [];
while (list) {
newArray.push(list.value);
list = list.rest;
}
return newArray;
}
However, even this code has the drawback that it is mixing the traversal of the list with the building of the array of values. It would be better to separate those:
function traverseList(list, fn) {
while (list) {
fn(list.value);
list = list.rest);
}
}
Now you can write listToArray as
function listToArray(list) {
var newArray = [];
traverseList(list, value => newArray.push(value));
return newArray;
}
I might also write this as a generator:
function* forList(list) {
while (list) {
yield list.value;
list = list.rest;
}
}
Now I don't even need a separate function to create the array of values, because I can just write
[...forList(list)]

javascript iterate through and map a multi-dimensional array

I want to apply a function to each of the values at all levels of the array:
arr = [[1,2,3],[1,2,3],[[1,2],[1,2]],1,2,3]
for example, multiply all the values by 3, and map it in the same format as before so I get:
arr = [[3,6,9],[3,6,9],[[3,6],[3,6]],3,6,9]
What would be the best way to go about this?
I tried to use a recursive function:
function mapall(array){
array.map(function(obj){
if (Array.isArray(obj)===true) { return mapall(obj) }
else{ return obj*3 }
})
};
but when I run it I just get undefined, so I must be doing something not quite right??
Any ideas??
Thanks
Everything was working, but you forgot to return from array.map. Here's a cleaner version of your code:
var arr = [[1,2,3],[1,2,3],[[1,2],[1,2]],1,2,3];
function mapAll(array){
return array.map(function(item){
return Array.isArray(item) ? mapAll(item) : item * 3;
});
}
alert(JSON.stringify(mapAll(arr)));
Version with callback function where you can do with array elements what you want (el * 2 or something else)
var arr = [[1,2,3],[1,2,3],[[1,2],[1,2]],1,2,3];
function mapAll(array, cb) {
for (var i = 0, len = array.length; i < len; i++) {
if (Array.isArray(array[i])) {
mapAll(array[i], cb);
} else {
array[i] = cb(array[i]);
}
}
return array;
};
var res = mapAll(arr, function (el) {
return el * 3;
});
console.log(JSON.stringify(res));

How is `each` different from `for-loop` when returning a value?

I have my each function which I created to emulate Underscore.js's _each() for my Javascript study.
var each = function(list, iteratee) {
if (Array.isArray(list)) { // list is array
for (var i = 0; i < list.length; i++) {
iteratee(list[i], i, list);
}
} else if (list.constructor === Object) { // list is object
for (var key in list) {
iteratee(list[key], key, list);
}
}
};
Then I wanted to create a function find which is also available from Underscore.js. This function looks through each value in the list, returning the first one that passes a truth test (predicate), or
undefined if no value passes the test. The function returns as soon as it finds an acceptable element and doesn't traverse the entire list.
Here is my version of find that I came up with.
var find = function(list, predicate) {
each(list, function(elem){
if (predicate(elem)) {
return elem;
}
});
};
I thought it would return a value immediately after it has found a true value for an element that passes a test from an external predicate function. But instead, it's giving me an undefined.
Below code works as I expected. But why would they provide different output?
var find = function(list, predicate) {
if (Array.isArray(list)) { // list is array
for (var i = 0; i < list.length; i++) {
if (predicate(list[i])) {
return list[i];
}
}
} else if (list.constructor === Object) { // list is object
for (var key in list) {
if (predicate(list[key])) {
return list[key];
}
}
}
};
What I don't understand is that why doesn't each works as I expected when I included it in my find function. Wouldn't they simply different in terms of their style of expression? In other word, one is functional style, and another is not?
This is caused due to the lack of return statement.
Each function iterates over find but doesn't return anything. return statement in predicate returns the output to the each function where it is not expected
Example of working function:
var find = function(list, predicate) {
var res = undefined/null/whatever;
each(list, function(elem) {
if (predicate(elem)) {
res = elem;
}
});
return res;
};
However this function is not efficient as it won't stop when result is found
This has to do with how return works. Let's look at your code:
var find = function(list, predicate) {
// you pass list and an anonymous callback to `each`
each(list, function (elem) {
// if this condition is true
if (predicate(elem)) {
// return elem
return elem;
}
});
}
The problem is that return elem applies to the anonymous callback, not the find function.
If you want to be able to "break" the each loop, you can check the current state on each iteration of the for-loop within each.
// only going to write for arrays
var each = function (list, iteratee) {
for (var i = 0; i < list.length; i++) {
if (iteratee(list[i], i, list)) continue;
else break;
}
});
// then in find:
var find = function (list, predicate) {
var ret = null
each(list, function(elem) {
if (predicate(elem)) {
ret = elem;
return false; // return false, since we no longer wish to continue
}
});
return ret;
};
A second solution is to return from within the each loop:
var each = function (list, iteratee) {
for (var i = 0; i < list.length; i++) {
if (iteratee(list[i], i, list)) {
continue;
} else {
return list[i];
}
}
// didn't find anything, so `return null`
return null;
});
var find = function (list, predicate) {
return each(list, function(elem) {
// if `predicate`, return false, so the loop breaks
return !predicate(elem);
});
};
The only problem I have with this solution is that it distorts the meaning of each. each intuitively means "go through everything," which the second solution doesn't necessarily do.

Trying to return up a recursive combinations function without getting 'undefined'

When I call this with [1,2,3,4], it returns undefined and I'm not understanding why. The goal is for it to return true if any combination of numbers in the array add up to the maximum number in the array, and false if it's not possible.
function ArrayAdditionI(arr) {
var max = Math.max.apply(null, arr);
arr.splice(arr.indexOf(max), 1);
var sum = function(arr) { return arr.reduce(function(a,b) { return a + b; }); };
function combos(arr) {
var f = function(prefix, arr) {
for (var i = 0; i < arr.length; i++) {
var clone = prefix.slice(0);
clone.push(arr[i]);
if (sum(clone) == max) { return true; }
return f(clone, arr.slice(i+1));
}
}
return f([], arr);
}
return combos(arr);
}
f is returning undefined when it is called with an empty arr! You will need to explicitly return false if none of the tests in the loop returned from the function. And you must not return false on the first occasion of the loop, but break only when you found true and continue the loop elsewhile.
To fix this, you'd have something like
function combos(arr) {
function f(prefix, arr) {
for (var i = 0; i < arr.length; i++) {
var clone = prefix.slice(0);
clone.push(arr[i]);
if (sum(clone) == max) return true;
if (f(clone, arr.slice(i+1))) return true;
}
return false;
}
return f([], arr);
}
However, also your recursion scheme with the loop looks a bit complicated. I would rather go with the naive enumeration of the "binary tree", where the nodes of each level decide whether the current item will be included in the to-be-tested subset:
function ArrayAdditionI(arr) {
var max = Math.max.apply(null, arr);
arr.splice(arr.indexOf(max), 1);
var sum = function(arr) { return arr.reduce(function(a,b) { return a + b; }, 0); };
function f(subset, arr) {
return arr.length
? f(subset, arr.slice(1)) || f(subset.concat([arr[0]]), arr.slice(1))
: sum(subset) == max
}
return f([], arr);
}
It seems that you don't test all the possible combination.
Here you will test 1+2+3, 2+3, 3 but never 1+3.
Do you really want to have a recursive function here ?
There may be some more simple way to find that.
function ArrayAdditionI(arr) {
var max = Math.max.apply(null, arr);
arr.splice(arr.indexOf(max), 1);
var res = arr.filter(function(num, idx) {
var combination = false;
// Check all combination non previously tested
for (var i = idx; i < arr.length - 1; i++) {
if (num + arr[i+1] === max) {
combination = true;
break;
}
}
return combination;
});
return res.length > 0;
}
The problem is your f function is not ever hitting the
if (sum(clone) == max) { return true; }
line of code so it will just keep recursively calling until arr.length == 0 and it will return undefined.
Your variables torf and results are unused, maybe you forgot to do something with them?

fastest way to detect if duplicate entry exists in javascript array?

var arr = ['test0','test2','test0'];
Like the above,there are two identical entries with value "test0",how to check it most efficiently?
If you sort the array, the duplicates are next to each other so that they are easy to find:
arr.sort();
var last = arr[0];
for (var i=1; i<arr.length; i++) {
if (arr[i] == last) alert('Duplicate : '+last);
last = arr[i];
}
This will do the job on any array and is probably about as optimized as possible for handling the general case (finding a duplicate in any possible array). For more specific cases (e.g. arrays containing only strings) you could do better than this.
function hasDuplicate(arr) {
var i = arr.length, j, val;
while (i--) {
val = arr[i];
j = i;
while (j--) {
if (arr[j] === val) {
return true;
}
}
}
return false;
}
There are lots of answers here but not all of them "feel" nice... So I'll throw my hat in.
If you are using lodash:
function containsDuplicates(array) {
return _.uniq(array).length !== array.length;
}
If you can use ES6 Sets, it simply becomes:
function containsDuplicates(array) {
return array.length !== new Set(array).size
}
With vanilla javascript:
function containsDuplicates(array) {
return array
.sort()
.some(function (item, i, items) {
return item === items[i + 1]
})
}
However, sometimes you may want to check if the items are duplicated on a certain field.
This is how I'd handle that:
containsDuplicates([{country: 'AU'}, {country: 'UK'}, {country: 'AU'}], 'country')
function containsDuplicates(array, attribute) {
return array
.map(function (item) { return item[attribute] })
.sort()
.some(function (item, i, items) {
return item === items[i + 1]
})
}
Loop stops when found first duplicate:
function has_duplicates(arr) {
var x = {}, len = arr.length;
for (var i = 0; i < len; i++) {
if (x[arr[i]]) {
return true;
}
x[arr[i]] = true;
}
return false;
}
Edit (fix 'toString' issue):
function has_duplicates(arr) {
var x = {}, len = arr.length;
for (var i = 0; i < len; i++) {
if (x[arr[i]] === true) {
return true;
}
x[arr[i]] = true;
}
return false;
}
this will correct for case has_duplicates(['toString']); etc..
var index = myArray.indexOf(strElement);
if (index < 0) {
myArray.push(strElement);
console.log("Added Into Array" + strElement);
} else {
console.log("Already Exists at " + index);
}
You can convert the array to to a Set instance, then convert to an array and check if the length is same before and after the conversion.
const hasDuplicates = (array) => {
const arr = ['test0','test2','test0'];
const uniqueItems = new Set(array);
return array.length !== uniqueItems.size();
};
console.log(`Has duplicates : ${hasDuplicates(['test0','test2','test0'])}`);
console.log(`Has duplicates : ${hasDuplicates(['test0','test2','test3'])}`);
Sorting is O(n log n) and not O(n). Building a hash map is O(n). It costs more memory than an in-place sort but you asked for the "fastest." (I'm positive this can be optimized but it is optimal up to a constant factor.)
function hasDuplicate(arr) {
var hash = {};
var hasDuplicate = false;
arr.forEach(function(val) {
if (hash[val]) {
hasDuplicate = true;
return;
}
hash[val] = true;
});
return hasDuplicate;
}
It depends on the input array size. I've done some performance tests with Node.js performance hooks and found out that for really small arrays (1,000 to 10,000 entries) Set solution might be faster. But if your array is bigger (like 100,000 elements) plain Object (i. e. hash) solution becomes faster. Here's the code so you can try it out for yourself:
const { performance } = require('perf_hooks');
function objectSolution(nums) {
let testObj = {};
for (var i = 0; i < nums.length; i++) {
let aNum = nums[i];
if (testObj[aNum]) {
return true;
} else {
testObj[aNum] = true;
}
}
return false;
}
function setSolution(nums) {
let testSet = new Set(nums);
return testSet.size !== nums.length;
}
function sortSomeSolution(nums) {
return nums
.sort()
.some(function (item, i, items) {
return item === items[i + 1]
})
}
function runTest(testFunction, testArray) {
console.log(' Running test:', testFunction.name);
let start = performance.now();
let result = testFunction(testArray);
let end = performance.now();
console.log(' Duration:', end - start, 'ms');
}
let arr = [];
let setSize = 100000;
for (var i = 0; i < setSize; i++) {
arr.push(i);
}
console.log('Set size:', setSize);
runTest(objectSolution, arr);
runTest(setSolution, arr);
runTest(sortSomeSolution, arr);
On my Lenovo IdeaPad with i3-8130U Node.js v. 16.6.2 gives me following results for the array of 1,000:
results for the array of 100,000:
Assuming all you want is to detect how many duplicates of 'test0' are in the array. I guess an easy way to do that is to use the join method to transform the array in a string, and then use the match method.
var arr= ['test0','test2','test0'];
var str = arr.join();
console.log(str) //"test0,test2,test0"
var duplicates = str.match(/test0/g);
var duplicateNumber = duplicates.length;
console.log(duplicateNumber); //2

Categories

Resources