javascript iterate through and map a multi-dimensional array - javascript

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));

Related

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?

Finding my object inside JSON by its ID

[
{"ID":"5","Name":"Jay"},
{"ID":"30","Name":"Sharon"},
{"ID":"32","Name":"Paul"}
]
So I have this kind of JSON.
I need to easily supply the value for a required key.
For example:
30 would yield => "Sharon"
5 would yield => "Jay"
etc. What is the right way to do this?
Iterate the array and check if the ID matches
function getById(id) {
var O = null;
for (var i=0; i<arr.length; i++) {
if ( arr[i].ID == id ) return O = arr[i];
}
return O;
}
getById('30'); // returns {"ID":"30","Name":"Sharon"}
FIDDLE
or in newer browsers:
function getById(arr, id) {
return arr.filter(function(o) { return o.ID == id });
}
FIDDLE
Try a linear search:
var searchId = "30";
for(var i = 0; i < json.length; i++)
{
if(json[i].ID == searchId)
{
// Found it.
//
break;
}
}
If the IDs will be unique, and if you're going to need to do this frequently, then you may want to convert your collection to key/value pairs where the ID is the key.
var byId = data.reduce(function(res, obj) {
res[obj.ID] = obj;
return res
}, {});
Now you can simply use the ID to look up the object.
var target = byId["30"];
You could probably just write something to loop through it.
var data = [ {"ID":"5","Name":"Jay"},{"ID":"30","Name":"Sharon"}, {"ID":"32","Name":"Paul"} ];
for(var i in data){
if(data[i]["ID"] == 30){
return data[i]["Name"];
}
}
undersocre.js can find a object in collection by one line code
Reference: http://underscorejs.org/#find
Code:
var people = [
{"ID":"5","Name":"Jay"},
{"ID":"30","Name":"Sharon"},
{"ID":"32","Name":"Paul"}
];
_.find(people, function(person) { return person.ID === '5'; });
FIDDLE

How to find array in array and remove it?

var arr = [["test","1"],["demo","2"]];
// $.inArray() ???
// .splice() ???
// $.each() ???
$("code").html(JSON.stringify(arr));
If I will find matching array by "test" (unique) keyword , I will remove ["test","1"]
So arr after removed will be [["demo","2"]]
How can I do that ?
Playground : http://jsbin.com/ojoxuy/1/edit
This is what filter is for:
newArr = arr.filter(function(item) { return item[0] != "test" })
if you want to modify an existing array instead of creating a new one, just assign it back:
arr = arr.filter(function(item) { return item[0] != "test" })
Modificator methods like splice make code harder to read and debug.
You could do something like this:
function remove(oldArray, itemName) {
var new_array = [];
for(var i = 0, len = oldArray.length; i < len; i++) {
var arr = oldArray[i];
if (arr[0] != itemName) new_array.push(arr);
}
return new_array;
}
And call it like this:
var arr = [["test","1"],["demo","2"]];
var new_arr = remove(arr,'test');
I'm making assumptions here and not doing any real error checking but you get the idea.
Perhaps something like:
for(var i = 0; i < arr.length; i++) {
if(arr[i][0] == "test") {
arr.splice(i, 1);
break;
}
}
var arr = [["test","1"],["demo","2"]];
function checkArrayElements(a, index, arr) {
if(a[0]=="test"){
delete arr[index];
arr.splice(index,index+1);
}
}
arr.forEach(checkArrayElements);
$("code").html(JSON.stringify(arr));
NOTE: This removes any inner array in arr with the 0 element = "test"
Check this one
function rmEl(a,v)
{
for(var i=0;i<a.length;i++)
{
if(a[i][0]==v)
{
a.splice(i,i+1);
i=-1;
}
$("code").html(JSON.stringify(a));
}
return a;
}

Javascript: union of multiple arguments

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));

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