I just wanted to know if anyone knew of a simple function to check if a given array is multidimensional and, if possible, also see how many dimensions it has.
Here is a sample array:
{"name":"Tiradentes","id":"234534gfgdgd4y5hy","areas":{{"name":"Apucarana", "id":"fh4oth98f894nf89h4"},{etc..., etc....}}}
Quite (I suppose) simple function counting dimensions BOTH in Arrays and array-like Objects (like in your case):
function countDimensions(arr)
{
if (! (arr && typeof arr === 'object') ) {
return 0;
}
var maxCount = Math.max.apply(null, $.map(arr, function(n) {
return countDimensions(n);
}));
return 1 + (isFinite(maxCount) ? maxCount : 0);
}
jQuery is used, as 0) it's mentioned in tags, 1) naive checking for Array with instanceof Array can lead to tricky results in multiframe apps, 2) $.map is a convenient shortcut for moving through all the array values.
It can be rewritten in plain JS, of course, but it won't be as compact. )
Had to use isFinite check, as protomax-ing an empty array gives -Infinite.
try with this
var dims = 0;
function getArrayDimensions(arr) {
if(typeof(arr) != 'array') return false;
for(var i = 0; i < arr.length; i++) {
if(typeof(arr[i]) == 'array') dims++;
dims += getArrayDimensions(arr[i]);
}
return dims;
}
JavaScript arrays are not really multi-dimensional, they are lists that can consist of different data types. So you only can determine the depth of an array by testing how often your array contains another array:
var isArray = Array.isArray || function(a) {
return a instanceof Array || Object.prototype.toString.call(a) == "[object Array]";
};
function getDepth(o) {
for (var i=0; isArray(o); o=o[0])
i++;
return i;
}
A more restrictive implementation would count the levels on which all slots of the array contain other arrays, instead of only inspecting the first one. Results would differ for [[], 0] for example.
function getDepth(o) {
if (!isArray(o))
return 0;
return 1 + (o.length && Math.min.apply(null, o.map(getDepth)));
}
Related
I'm trying to create an Array with unique objects. I've got json data from a tounament that I want to order by pools. Each match got his own pooldata so he will push the pool data of each match to the array. This will create an Array of 5 of the same pool values. This is my code:
var arr = [];
for (var i = 0; i < data.objects.length; i++){
obj = {
poolId: data.objects[i].pool.id,
poolLetter: data.objects[i].pool.name
};
if (arr.indexOf(obj) == -1) {
arr.push(obj);
}else{}
}
The problem is that the obj you are generating in the loop is not going to be the same object inside your arr array, they will have different signatures, so you can't use indexOf in this instance.
Instead, you will have to loop over the arr array to see if you can find any elements with the same poolId or poolLetter:
var arr = [];
for (var i = 0; i < data.objects.length; i++){
obj = {
poolId: data.objects[i].pool.id,
poolLetter: data.objects[i].pool.name
};
// Do the insert if arr doesn't already contain this poolId
if(!arrayContains(arr, obj.poolId)) arr.push(obj);
}
// Helper function to find an instance of poolId in the given array
function arrayContains(arr, poolId) {
for(var x = 0; x < arr.length; x++) {
if(arr[x].poolId === poolId) {
return true;
}
}
return false;
}
Here is a fiddle which demonstrates the above.
indexOf compares searchElement to elements of the Array using strict equality (the same method used by the ===, or triple-equals, operator).
then
var obj1 = { a:1};
var obj2 = { a:1};
obj1 === obj2; // wrong
when you write "var obj1={a:1}" ,javascript create a new object.
You can use Array Prototype. Just pass the object.
Array.prototype.contains = function(obj) {
var i = this.length;
while (i--) {
if (this[i] === obj) {
return true;
}
}
return false;
}
Use the following
alert([1, 2, 3].contains(2)); //true
alert([1, 2, 3].contains('2')); //false
There is also a jQuery solution. I know you didn't asked for a jQuery answer. But maybe you want use it.
jQuery.inArray() returns the index of a specified value and returns -1 if not found.
http://api.jquery.com/jQuery.inArray/
I am trying to have fun with my buddy who solved the problem mentioned in 8m 7s, and for me it is already 20m gone. I can't figure out how to handle unlimited nested array in javascript.
The problem is this:
// i will be an array, containing integers, strings and/or arrays like itself.
// Sum all the integers you find, anywhere in the nest of arrays.
So
arraySum([[1,2,false],'4','5']) will return 3 (passed)
arraySum([[1,2,3],4,5]) will return 15 (passed)
arraySum([[[[[[[[[1]]]]]]]], 1]) will return 2 (failed)
The code I wrote is:
function arraySum(i) {
sum = 0;
tmp =0;
for (var a=0; a<i.length; a++){
if (i[a] instanceof Array) {
ar = i[a];
for (var j=0; j<ar.length; j++){
tmp +=ar[j];
}
}
if (typeof i[a] == "number")
sum += i[a];
console.log(sum);
}
return sum + tmp;
}
As you can see it does not handle the last situation that I failed as I can't figure out how to handle unlimited nest in JS.
Any idea will be much appreciated.
Also try to finish it before 8m 7s, which my buddy finished in.
Inside of the if (i[a] instanceof Array) { part, you'll have to use recursion to operate on nested arrays with the same arraySum function, not just use another loop. Try this:
var arraySum = (function () {
"use strict";
var sumFunc, isArray;
sumFunc = function (arr) {
var sum, i, j, cur, toAdd;
sum = 0;
for (i = 0, j = arr.length; i < j; i++) {
cur = arr[i];
toAdd = 0;
if (isArray(cur)) {
toAdd = sumFunc(cur);
} else if (typeof cur === "number") {
toAdd = cur;
}
sum += toAdd;
}
return sum;
};
isArray = Array.isArray || function (obj) {
return Object.prototype.toString.call(obj) === "[object Array]";
};
return sumFunc;
}());
DEMO: http://jsfiddle.net/Q7JPM/1
The function just loops through all items in an array, and returns the sum of any numbers found inside. If the item is an array itself, it calls arraySum and passes that array...adding the result to the sum. If it's a number, it simply adds that to the sum.
You have to use recursion:
http://jsfiddle.net/HMnat/2
function arraySumRec(theArray)
{
var sum=0;
for (var i=0;i<theArray.length;i++)
{
if (theArray[i] instanceof Array)
{
sum=sum+arraySumRec(theArray[i]);
}
else
{
if (typeof(theArray[i])=="number")
{
sum=sum+theArray[i];
}
}
}
return sum;
}
Took me 3 minutes 47 seconds (due to a typo, ha ha).
The Javascript Array reduce method is perfect for solving this kind of problem. The reduce method takes a function with at least two arguments: the accumulator and the current element of the array. In the body of the function, you specify how each element should affect the accumulator. The second argument to the function is the starting value of the accumulator.
function sum(x) {
return x.reduce(function(accumulator, currentValue) {
if (typeof currentValue === "number") {
return accumulator + currentValue;
} else if (currentValue instanceof Array) {
return accumulator + sum(currentValue);
} else {
return accumulator;
}
}, 0);
}
JSFIDDLE
The function sum takes an array, and the reduce method reduces it to a single value. In the "else if" branch, where we find a nested array, we can simply call sum on it, get back a single value, and add that to our accumulator. In the "else" branch, we haven't found the kinds of values we're interested in so we leave the accumulator unchanged.
The documentation at MDN provides a good explanation of Array reduce with examples.
function arraySum(i) {
var l = i.length, sum = 0;
while (l--) {
if (typeof i[l] !== 'number' && !(i[l] instanceof Array)) continue;
if (i[l] instanceof Array) { sum += arraySum(i[l]); continue; }
sum += i[l];
}
return sum;
}
Non-Recursive using a stack.
function arraySum(arr)
{
var sum = 0;
while(arr.length != 0)
{
var value = arr.pop();
if(value instanceof Array)
{
for (i= 0; i< value.length; ++i)
arr.push(value[i]);
}
else if(typeof value === "number")
sum += value;
}
return sum;
}
var arr = [1, 2, [3, 4, [[[5]]]]];
console.log(arraySum(arr));
If we focus on the right parts, we can save ourselves the tedium from focusing on the wrong parts -
function arraySum (t)
{ switch (t?.constructor)
{ case Array:
return t.reduce((r, v) => r + arraySum(v), 0)
case Number:
return t
default:
return 0
}
}
console.log(arraySum([[1,2,false],'4','5']))
console.log(arraySum([[1,2,3],4,5]))
console.log(arraySum([[[[[[[[[1]]]]]]]], 1]))
3
15
2
If ?. is not yet supported in your environment, you can swap it out -
switch (t?.constructor)
switch (t && t.constructor) // <- where ?. is unsupported
Edit: it took me 2,769 days to answer the question but only a few minutes to write it :D
I'm working on project where I have array simmilar to this
var sortBp = [ 'height58em', 'width480px', 'width768px', 'width959px', 'width767px', 'width767px' ];
I want to sort this array in ascending order while eleminating repeated values so that result should be
var sortBp = [ 'height58em', 'width480px', 'width767px', 'width768px', 'width959px' ];
I'm using following function to sort array in ascending array but how do I eliminate immediate values ?? (In above case 'width767px')
var sortBp = bpArrays.sort(function(a, b) {
a = a.replace(/[a-z]/g, '');
b = b.replace(/[a-z]/g, '');
return a - b;
});
Firstly, you can't eliminate elements while sorting. You have to sort the array first, then remove duplicates. Your solution using Array.prototype.filter and Array.prototype.indexOf might be good for unsorted array, but since your array is sorted, it's overhead here(takes O(n) for each). Instead you can just loop through the array and compare element with previous one.
function uniq(array) {
var i, l = array.length, result = [];
for (i = 0; i < l; i++) {
if (result[result.length - 1] != array[i]) {
result.push(array[i]);
}
}
return result;
}
This is same with Underscore and Prototype's uniq() implemention.
Last note: remember that this will work fine with sorted array only.
If this is not homework, i.e. you aren't required to implement the algorithm yourself, you could use a combination of sortBy and uniq functions.
sortBp = [ 'height58em', 'width480px', 'width768px', 'width959px', 'width767px', 'width767px' ];
_.uniq(_.sortBy(sortBp, function(x) {return x;}), true);
This returns:
["height58em", "width480px", "width767px", "width768px", "width959px"]
Note that this will be sort by lexical order, and if you do not wish this to happen, simply substitute the identity function function(x) {return x;} with your own logic.
Array.prototype.contains = function(k) {
for ( var p in this)
if (this[p] === k)
return true;
return false;
};
inputArray.sort();
function uniqueArray(inputArray){
result=[];
for(var i in inputArray){
if(!result.contains(inputArray[i])){
result.push(inputArray[i]);
}
}
return result;
}
Found It !!
var sortBp = [ 'height58em', 'width480px', 'width768px', 'width959px', 'width767px', 'width767px' ];
uniqueArray = sortBp.filter(function(elem, pos) {
return sortBp.indexOf(elem) == pos;
});
EDIT :
I have found another solution here : http://dreaminginjavascript.wordpress.com/2008/08/22/eliminating-duplicates/
function eliminateDuplicates(arr) {
var i, len=arr.length, out=[], obj={};
for (i=0;i<len;i++) {
obj[arr[i]]=0;
}
for (i in obj) {
out.push(i);
}
return out;
}
I am wondering how I can check if a duplicate pair of values in an array exist as part of a larger array in javascript. You can see there is a duplicate pair of [1,2] - so the function should just return true. i.e
var arr = [[1,2], [3,4], [5,6], [7,8], [9,10], [11,12], [13,14], [1,2]]
I have tried using this logic which gives me a clean array and a "true"
var unique = [];
var done = []; var dup = false;
for(var x = 0; x < arr.length; x++) {
var myStr = arr[x].toString();
if(done.indexOf(myStr) != -1) {
// val already exist, ignore
dup = true;
continue;
}
done.push(myStr);
unique.push(arr[x]);
}
But I was wondering if there is something more elegant using Underscore ?
The shortest way would be to use _.uniq and JSON.stringify:
function unique(arr) {
return _.uniq(arr, JSON.stringify).length === arr.length;
}
But that doesn't short-circuit, so it's somewhat slow compared to the other ways you could do it. Tomalak's second function should be faster.
Well, uniq seems like a good fit
function containsDuplicates(arr) {
return arr.length !== _.uniq(arr, function (item) { return item.toString(); }).length;
}
You should use Blender's version of this function. It's shorter and safer.
BTW, your code should look more like this:
function containsDuplicates(arr) {
var index = {}, i, str;
for(i = 0; i < arr.length; i++) {
// you could use arr[i].toString() here, but JSON.stringify()
// is a lot safer because it cannot create ambiguous output.
str = JSON.stringify(arr[i]);
if (index.hasOwnProperty(str)) {
return true;
} else {
index[str] = true;
}
}
return false;
}
Note that this is probably more efficient than the underscore one-liner.
Although stringify is the answer most of the time, it still has its issues, for example {"x":1,"y":2} and {"y":2,"x":1} are considered different. If you need a 100% accurate comparison, there's no other way as to store already processed objects and deep compare them (luckily, underscore provides an utility for this).
uniq2 = function(xs) {
return _.reduce(xs, function(result, x) {
if(!_.any(result, _.partial(_.isEqual, x)))
result.push(x);
return result;
}, []);
}
Test:
var arr = [[1,2], [3,4], "1,2", "[1,2]", [1,2], {x:1,y:2}, {y:2,x:1}]
console.log(uniq2(arr))
// [[1,2],[3,4],"1,2","[1,2]",{"x":1,"y":2}]
This is going to be quadratic in the worst case, but there's no other way.
In Javascript, if I have an array of arrays, like the following:
X = [ [1,2,3,4],
[1,1,2,3],
[1,1,3],
[1,4],
[2,1,2],
[2,2]
]
Javascript sorts my array, comparing first entry first, then second, and so on, so that X.sort() returns the following:
[ [1,1,2,3],
[1,1,3],
[1,2,3,4],
[1,4],
[2,1,2],
[2,2]
]
Which is what I want. The problem is that the comparison operator for comparing the elements in the arrays is lexicographical, so [10,2] < [2,2], and, for example,
[[10,2],[1,1,3],[2,2]].sort() -> [[1,1,3],[10,2],[2,2]]
I need it to sort numerically, so that I get a sorted array of [[1,1,3],[2,2],[10,2]].
I tried using a comparison function of function(a,b){return (a-b) }, which would work for sorting an array of numbers, but this fails to properly sort my array, which makes sense (I think) because [10,2] - [1,1,3] yields NaN
How do I go about sorting an array of numeric arrays?
As I said in my comment, the sort function needs to account for the fact that it's receiving arrays as arguments and not plain values. So you need to handle them accordingly.
I suggest this;
var compFunc = function (a, b) {
var len = a.length > b.length ? b.length : a.length;
for(var i=0; i<len; ++i) {
if(a[i] - b[i] !== 0)
return a[i] - b[i];
}
return (a.length - b.length);
};
It first tries to look for differences in the common length of the two arrays. If the common length is exactly the same, then it sorts on the basis of array length. Here's a working fiddle.
What you want is to run a natural sort. For your compare function, replace it with the script mentioned in this article
http://my.opera.com/GreyWyvern/blog/show.dml/1671288
When you do X.sort(), Javascript is comparing your individual arrays as strings. It's basically doing a.toString().localeCompare(b.toString()). This is not what you want.
a.toString() is usually the same as a.join(',')
What I would do is compare each element in the arrays by using a for loop.
Something like this:
X.sort(function(a,b){
// Start off assuming values are equal
var ret = 0;
// Loop through a
for(var a_i = 0, a_length = a.length; a_i < a_length; a_i++){
// If b is shorter than a, it comes first
if(typeof b[a_i] === 'undefined'){
ret = 1;
break;
}
// if the element in a and b are *not* the same, then we can sort
else if(a[a_i] !== b[a_i]){
ret = a[a_i] - b[a_i];
break;
}
}
return ret;
});
You need to sort and compare between the 2 arrays:
http://jsfiddle.net/pXzB6/
var arr = [[10,2],[1,1,3],[2,2]];
arr.sort(function(a,b){
for(var i=0;i<a.length;i++){
var item_a = a[i];
for(var j=0;j<b.length;b++){
var item_b = b[j];
if(item_a == item_b){
continue;
}
else{
return item_a > item_b;
}
}
}
if(a.length == b.length){
return 0;
}
else{
return a.length > b.length;
}
});
console.log(arr);
var points = [40, 100, 1, 5, 25, 10];
points.sort(function(a, b){return a-b});
then the result is :
1,5,10,25,40,100
This is simplest way i think, it's worked.