Case: We have 'n' number of arrays stored in an array (Array of Arrays). Now that each child array in this parent array can have elements that may or may not be present in other child arrays. Output - I need to create an array which has the all the elements present in all the child arrays excluding the duplicates.
I do not want to concatenate all the arrays into a single array and use unique method to filter out. I need to create unique array then and there during iteration.
Ex:
var a[] = [1,2,3,4,5];
var b[] = [1,2,7,8];
var c[] = [1,2,3,4,5,6,7,8];
var d[] = [9,10,11,12];
var arr[] = [a,b,c,d]
Output must be [1,2,3,4,5,6,7,8,9,10,11,12]
P.S: I can concat the arrays and use jquery unique function to resolve this, but i need a solution in javascript alone. Thanks
You can use array#reduce to flatten your array and then use Set to get distinct values and use array#from to get back array from Set.
var a = [1,2,3,4,5];
var b = [1,2,7,8];
var c = [1,2,3,4,5,6,7,8];
var d = [9,10,11,12];
var arr = [a,b,c,d]
var result = Array.from(new Set(arr.reduce((r,a) => r.concat(a))));
console.log(result);
Try using .filter when adding each array to the final one, filtering out the duplicates:
a.filter(function(item) {
return !finalArray.contains(item));
});
Answer using Sets:
var a = [1,2,3,4,5];
var b = [1,2,7,8];
var c = [1,2,3,4,5,6,7,8];
var d = [9,10,11,12];
var concat = a.concat(b).concat(c).concat(d);
var union = new Set(concat);
//console.log(union);
ES6 Answer:
let a = new Set([1,2,3,4,5]);
let b = new Set([1,2,7,8]);
let c = new Set([1,2,3,4,5,6,7,8]);
let d = new Set([9,10,11,12]);
let arr = new Set([...a,...b,...c,...d]);
//Result in arr.
Whats going on???
From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set:
The Set object lets you store unique values of any type, whether
primitive values or object references.
So when we initialise Sets passing arrays to the constructor we basically ensure that there are no duplicate values.
Then in the last line, we concat all the Sets we initialised prior into a final set.
The ... notation converts the Set into an array, and when we pass the 4 arrays to the constructor of the Set they get concatenated and a Set of their unique values is created.
Here is a functional alternative written in ES5.
var flatten = function(list) {
return list.reduce(function(acc, next) {
return acc.concat(Array.isArray(next) ? flatten(next) : next);
}, []);
};
var unique = function(list) {
return list.filter(function(element, index) {
return list.indexOf(element) === index;
})
}
var a = [1,2,3,4,5];
var b = [1,2,7,8];
var c = [1,2,3,4,5,6,7,8];
var d = [9,10,11,12];
var arr = [a,b,c,d];
var result = unique(flatten(arr));
console.log(result);
If you support ES6, arrow function can make that code even shorter.
Here is a solution that uses a plain object for resolving duplicates, and only uses basic ES3 JavaScript. Runs in IE 5.5 and higher, and with O(n) time complexity.
function uniques(arr) {
var obj = {}, result = [];
for (var i = 0; i < arr.length; i++) {
obj[arr[i]] = true;
}
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) result.push(+prop);
}
return result;
}
// Example use
var a = [1,2,3,4,5],
b = [1,2,7,8],
c = [1,2,3,4,5,6,7,8],
d = [9,10,11,12];
var result = uniques(a.concat(b, c, d));
console.log('Result: ' + result);
As an object can only have a unique set of properties (no duplicates), the use of all array values as properties in an object will give you an object with a property for each unique value. This happens in the first loop. NB: the value given to those properties is not relevant; I have used true.
Then the result is just the conversion of those properties back to array values. This happens in the second loop.
var a = [1,2,3,4,5];
var b = [1,2,7,8];
var c = [1,2,3,4,5,6,7,8];
var d = [9,10,11,12];
var result = a.concat(b,c,d);
function remvDup(result){
var tmp = [];
for(var i = 0; i < result.length; i++){
if(tmp.indexOf(result[i]) == -1){
tmp.push(result[i]);
}
}
return tmp;
}
console.log(remvDup(result));
Becuase the OP mentioned that he cannot use 'Set' as it is not supported on the targeted browsers, I would recommand using the 'union' function from the lodash library.
See union's documentation here
Related
What's the best way to convert this array of comma separated values
[ 'com--test,LFutx9mQbTTyRo4A9Re5ksjdnfsI4cKN4q2,on',
'com--fxtrimester,SEzlksdfMpW3FxkSbzL7eo5MmqkPczCl2,on',
'com--fxtrimester,LFutx9mQbTTyRoldksfns4A9Re5I4cKN4q2,on' ]
Into three arrays?
[ 'com--test', [ LFutx9mQbTTyRo4A9Re5Ilsdf4cKN4q2', ['on',
'com--fxtrimester', SEzMpW3FxkSbzL7eo5MmlkdfqkPczCl2', 'on',
'com--fxtrimester' ] LFutksdfx9mQbTTyRo4A9Re5I4cKN4q2 ] 'on']
I was trying something like:
var indexToSplit = unique.indexOf(',');
var status = unique.slice(3, indexToSplit - 1);
var use = unique.slice(2, indexToSplit - 2);
var pros = unique.slice(0, indexToSplit - 3);
console.log(pros);
But I figured that is wrong ... any help is appreciated!
You will have to loop over array and use string.split to get seperate parts.
Once you have seperate parts, you can push them to necessary array;
var d = [ 'com--test,LFutx9mQbTTyRo4A9Re5ksjdnfsI4cKN4q2,on',
'com--fxtrimester,SEzlksdfMpW3FxkSbzL7eo5MmqkPczCl2,on',
'com--fxtrimester,LFutx9mQbTTyRoldksfns4A9Re5I4cKN4q2,on'];
var result = [[],[],[]];
var len = 3;
d.forEach(function(str, i){
var _tmp = str.split(',');
for (var i = 0; i<len; i++){
result[i].push(_tmp[i])
}
})
console.log(result)
A little generic way.
Loop over data and split each string using comma(,)
Loop over split values and check if necessary array exists.
If not, initialise array, but you cannot do p[i] = [] as this will push to first value. You will have to also initialise all previous values. For this, you can use new Array(length). By default, if length is greater than 0, all indexes will be initialise to undefined.
Now push to necessary array. Position will be maintained.
var d = ['com--test,LFutx9mQbTTyRo4A9Re5ksjdnfsI4cKN4q2,on',
'com--fxtrimester,SEzlksdfMpW3FxkSbzL7eo5MmqkPczCl2,on',
'com--fxtrimester,LFutx9mQbTTyRoldksfns4A9Re5I4cKN4q2,on,test'
];
var result = d.reduce(function(p, c, index) {
var _tmp = c.split(',');
for (var i = 0; i < _tmp.length; i++) {
// Check if position not defined.
if (p[i] === undefined)
// Initialize array and add default (undefined) to all elements before current element
p[i] = new Array(index);
p[i].push(_tmp[i])
}
return p;
}, [])
console.log(result)
With map this becomes:
for positions X out of 0, 1 and 2:
convert each item in the list into an array, and choose the Xth item
var start = [ 'com--test,LFutx9mQbTTyRo4A9Re5ksjdnfsI4cKN4q2,on',
'com--fxtrimester,SEzlksdfMpW3FxkSbzL7eo5MmqkPczCl2,on',
'com--fxtrimester,LFutx9mQbTTyRoldksfns4A9Re5I4cKN4q2,on' ]
var out = [0,1,2].map(i =>
start.map(x => x.split(',')[i]) )
console.log(out)
Since your question does not ask for a more general case, i am safely assuming it for 3 array. We can use forEach function on array below code can be one amongst the possible solutions
var arr1 = [];
var arr2 = [];
var arr3 = [];
var x = ['com--test,LFutx9mQbTTyRo4A9Re5ksjdnfsI4cKN4q2,on', 'com--fxtrimester,SEzlksdfMpW3FxkSbzL7eo5MmqkPczCl2,on', 'com--fxtrimester,LFutx9mQbTTyRoldksfns4A9Re5I4cKN4q2,on']
x.forEach(function(data) {
var dataArray = data.split(',');
arr1.push(dataArray[0]);
arr2.push(dataArray[1]);
arr3.push(dataArray[2]);
});
console.log(arr1)
console.log(arr2)
console.log(arr3)
I have this:
var a = {};
a[1] = 1;
a[4] = 4;
console.log(JSON.stringify(a));
then I get:
{"1":1,"4":4}
but I want to get:
{1:1,4:4}
how to reach this? In other words, I want to keys be real int.
When you call JSON.stringify() method it creates a valid JSON string.
One of the rules for valid JSON is that every property should be in "quotes".
So thats why it is impossible to get such result as you want using JSON.stringify.
If you want to just convert such object to array it is possible, for example usin such function.
function numerableObjectToArr(obj) {
var result = [];
var keys = Object.keys(obj);
keys.forEach(function(item){
result.push(obj[item]);
})
return result;
}
var a = {};
a[1] = 1;
a[4] = 4;
numerableObjectToArr(a); // returns [1, 4]
But in this way you will just receive Array with values of existing properties in the obj.
But if your prop name means the index in the array, and you are sure that there will be always number as a prop name - you can improve this function:
function numerableObjectToArr(obj) {
var result = [];
var keys = Object.keys(obj);
keys.forEach(function(item){
result[+item] = obj[item]; //we put index, then we put value to that place in array
})
return result;
}
var a = {};
a[1] = 1;
a[4] = 4;
numerableObjectToArr(a); // returns [undefined, 1, undefined, undefined, 4]
I'm not sure you can do what you're trying to do the as the keys have to be string values. I'd advise having string name for your keys (i.e 1 = One, 2 = Two, etc). You could then try this:
var a = {};
a.one = 1;
a.two = 2;
a.three = 3;
a.four = 4;
console.log(JSON.stringify(a));
I hope this helps.
var a = {};
a[1] = 1;
a[4] = 4;
alert(JSON.stringify(a).replace(/\"([0-9]+)\":/g, '$1:'));
But it is kludge. JSON - has a string keys.
I have a question of JS arrays.
Example:
var fullArr = [1,2,3,4];
var partArr = [2,3];
var newArr = [];
We have a main array fullArr and a partial array partarr. I want to create a function/filter, which is looking for existing items in fullArr and not in partArr.
In this example above newArr must be equal to [1,4].
I've tried doing something like this, but it's not working properly.
for (var k in fullArray) { // [1,2,3,4]
for (var j in selectedArray) { // [1,4]
if (fullArray[k] == selectedArray[j]) {
newArray.splice(selectedArray[j] - 1, 1); // must be [2,3]
break;
}
}
}
What is a good way of making this? Thanks.
Here's one
var newArr = fullArr.filter(function(f) { // The filter() method creates a new array with all elements that pass the test implemented by the provided function.
return partArr.indexOf(f) == -1; // The indexOf() method returns the first index at which a given element can be found in the array, or -1 if it is not present.
})
to impress the girls, you can also
var newArr = fullArr.filter(function(f) {
return !~partArr.indexOf(f);
})
Here is the code for your requirement.
var fullArr = [1,2,3,4];
var partArr = [2,3];
var newArr = [];
for(var i=0;i<fullArr.length;i++){
if(partArr.indexOf(fullArr[i]) == -1)
newArr.push(fullArr[i]);
};
Here is the working Link
Hope it works :)
In fact, you want a common part between arrays. Obviously you can choose splice or indexOf to have O(n * m) or even O(m * n^2) performance. It's obviously suboptimal for any array larger than few elements
Or you can use objects as hash maps to find differences in (in worst case) O(n + m log m):
var fullArr = [1,2,3,4];
var partArr = [2,3];
var temporaryObject = Object.create(null);
partArr.forEach(el=>temporaryObject[el] = true); // temporaryObject after this operation is {"2": true, "3": true}
var newArr = fullArr.filter(el=>temporaryObject[el]);
In this example I have used ES6 feature called "arrow functions". It translates to following ES5 code:
var partArr = [2, 3];
var temporaryObject = Object.create(null);
partArr.forEach(function (el) {
temporaryObject[el] = true;
}); // temporaryObject after this operation is {"2": true, "3": true}
var newArr = fullArr.filter(function (el) {
return temporaryObject[el];
});
You can use the filter() function that works on arrays:
var newArr = fullArr.filter(function(val, i, arr) {
return partArr.indexOf(val) === -1;
});
This will return a new array containing the values of every iteration that returns true.
Should you ever need to do this on an object in the future a great way is to first convert the object keys to an array and then run the filter:
Object.keys(myObj).function(val, i, arr) {
return partArr.indexOf(val) === -1;
});
Here are few other approaches:
var fullArr = [1,2,3,4];
var partArr = [2,3];
var newArr = [];
1.
fullArr.map(function(element){
if(partArr.indexOf(element) === -1) newArr.push(element);
})
console.log(newArr);
2.
for(i in fullArr){
if(partArr.indexOf(fullArr[i]) === -1) newArr.push(fullArr[i]);
}
console.log(newArr);
3.
fullArr.forEach(function(element){
if(partArr.indexOf(element) === -1) newArr.push(element);
})
console.log(newArr);
This is my code, acting upon myArray:
var myArray = [];
var i;
for(i = 0; i < 20; i += 1) {
myArray.push(Math.random());
}
Is there a functional equivalent of the above that does without the dummy variable i?
Favorite answers:
while(myArray.push(Math.random()) < 20);
$.map(Array(20), Math.random);
for(var myArray = []; myArray.push(Math.random()) < 20;);
Not in ES5, there's no real functional equivalent to it, as you have to have something which has an amount of 20 to apply map to...
var my20ElementArray = [0,1,2,3,4,5,6,7,8,9,10];
var myArray = my20ElementArray.map(Math.random);
You could create an xrange-like function what is in Python but that would just hide this "unused" variable inside a function.
With JavaScript 1.7, you can use Array comprehensions for this task:
var myArray = [Math.random() for each (i in range(0, 20))];
However, with ES5.1 you can just use the Array constructor to generate an array of arbitrary length, and then map it to random numbers. Only drawback is that map() does not work with uninitialised values, so I first generate an Array of empty strings by using join and split:
var myArray = new Array(20).join(" ").split(" ").map(Math.random);
Ugly, but short. A maybe better (but less understandable) idea from Creating range in JavaScript - strange syntax:
var myArray = Array.apply(null, {length: 20}).map(Math.random);
Starting with #FelixKlings comment, one could also use this one-liner without the i loop variable:
for (var myArray=[]; myArray.push(Math.random()) < 20;);
// much better:
for (var myArray=[]; myArray.length < 20;) myArray.push(Math.random());
Are you looking for something as follows:
function makeArray(length, def) {
var array = [];
var funct = typeof def === "function";
while (array.push(funct ? def() : def) < length);
return array;
}
Then you can create arrays as follows:
var array = makeArray(100); // an array of 100 elements
var zero = makeArray(5, 0); // an array of 5 `0`s
In your case you may do something like:
var myArray = makeArray(20, Math.random);
See the following fiddle: http://jsfiddle.net/WxtkF/3/
how about this?
it's functionale style and it's very concise.
var makeRandomArray = function(n){
if (n == 0) return [];
return [Math.random()].concat(makeRandomArray(n-1));
};
console.log(makeRandomArray(20))
http://jsfiddle.net/YQqGP/
You could try:
var myArray = String(Array(20)).split(',')
.map( () => Math.random() );
Or extend the Array prototype with something like:
Array.prototype.vector = function(n,fn){
fn = fn || function(){return '0';};
while (n--){
this.push(fn());
}
return this;
}
// usage
var myArray = [].vector(20, () => Math.random());
Or try something funny:
var myArray = function a(n,fn){
return n ? a(n-1,fn).concat(fn()) : [];
}(20, () => Math.random())
Or use Array.from (ES>=2015)
Array.from({length: 20}).map(() => Math.random())
I have an array of items as follows in Javascript:
var users = Array();
users[562] = 'testuser3';
users[16] = 'testuser6';
users[834] = 'testuser1';
users[823] = 'testuser4';
users[23] = 'testuser2';
users[917] = 'testuser5';
I need to sort that array to get the following output:
users[834] = 'testuser1';
users[23] = 'testuser2';
users[562] = 'testuser3';
users[823] = 'testuser4';
users[917] = 'testuser5';
users[16] = 'testuser6';
Notice how it is sorted by the value of the array and the value-to-index association is maintained after the array is sorted (that is critical). I have looked for a solution to this, tried making it, but have hit a wall.
By the way, I am aware that this is technically not an array since that would mean the indices are always iterating 0 through n where n+1 is the counting number proceeding n. However you define it, the requirement for the project is still the same. Also, if it makes a difference, I am NOT using jquery.
The order of the elements of an array is defined by the index. So even if you specify the values in a different order, the values will always be stored in the order of their indices and undefined indices are undefined:
> var arr = [];
> arr[2] = 2;
> arr[0] = 0;
> arr
[0, undefined, 2]
Now if you want to store the pair of index and value, you will need a different data structure, maybe an array of array like this:
var arr = [
[562, 'testuser3'],
[16, 'testuser6'],
[834, 'testuser1'],
[823, 'testuser4'],
[23, 'testuser2'],
[917, 'testuser5']
];
This can be sorted with this comparison function:
function cmp(a, b) {
return a[1].localeCompare(b[1]);
}
arr.sort(cmp);
The result is this array:
[
[834, 'testuser1'],
[23, 'testuser2'],
[562, 'testuser3'],
[823, 'testuser4'],
[917, 'testuser5'],
[16, 'testuser6']
]
If I understand the question correctly, you're using arrays in a way they are not intended to be used. In fact, the initialization style
// Don't do this!
var array = new Array();
array[0] = 'value';
array[1] = 'value';
array[2] = 'value';
teaches wrong things about the nature and purpose of arrays. An array is an ordered list of items, indexed from zero up. The right way to create an array is with an array literal:
var array = [
'value',
'value',
'value'
]
The indexes are implied based on the order the items are specified. Creating an array and setting users[562] = 'testuser3' implies that there are at least 562 other users in the list, and that you have a reason for only knowing the 563rd at this time.
In your case, the index is data, and is does not represent the order of the items in the set. What you're looking for is a map or dictionary, represented in JavaScript by a plain object:
var users = {
562: 'testuser3',
16: 'testuser6',
834: 'testuser1',
823: 'testuser4',
23: 'testuser2',
917: 'testuser5'
}
Now your set does not have an order, but does have meaningful keys. From here, you can follow galambalazs's advice to create an array of the object's keys:
var userOrder;
if (typeof Object.keys === 'function') {
userOrder = Object.keys(users);
} else {
for (var key in users) {
userOrder.push(key);
}
}
…then sort it:
userOrder.sort(function(a, b){
return users[a].localeCompare(users[b]);
});
Here's a demo
You can't order arrays like this in Javascript. Your best bet is to make a map for order.
order = new Array();
order[0] = 562;
order[1] = 16;
order[2] = 834;
order[3] = 823;
order[4] = 23;
order[5] = 917;
In this way, you can have any order you want independently of the keys in the original array.
To sort your array use a custom sorting function.
order.sort( function(a, b) {
if ( users[a] < users[b] ) return -1;
else if ( users[a] > users[b] ) return 1;
else return 0;
});
for ( var i = 0; i < order.length; i++ ) {
// users[ order[i] ]
}
[Demo]
Using the ideas from the comments, I came up with the following solution. The naturalSort function is something I found on google and I modified it to sort a multidimensional array. Basically, I made the users array a multidimensional array with the first index being the user id and the second index being the user name. So:
users[0][0] = 72;
users[0][1] = 'testuser4';
users[1][0] = 91;
users[1][1] = 'testuser2';
users[2][0] = 12;
users[2][1] = 'testuser8';
users[3][0] = 3;
users[3][1] = 'testuser1';
users[4][0] = 18;
users[4][1] = 'testuser7';
users[5][0] = 47;
users[5][1] = 'testuser3';
users[6][0] = 16;
users[6][1] = 'testuser6';
users[7][0] = 20;
users[7][1] = 'testuser5';
I then sorted the array to get the following output:
users_sorted[0][0] = 3;
users_sorted[0][1] = 'testuser1';
users_sorted[1][0] = 91;
users_sorted[1][1] = 'testuser2';
users_sorted[2][0] = 47;
users_sorted[2][1] = 'testuser3';
users_sorted[3][0] = 72;
users_sorted[3][1] = 'testuser4';
users_sorted[4][0] = 20;
users_sorted[4][1] = 'testuser5';
users_sorted[5][0] = 16;
users_sorted[5][1] = 'testuser6';
users_sorted[6][0] = 18;
users_sorted[6][1] = 'testuser7';
users_sorted[7][0] = 12;
users_sorted[7][1] = 'testuser8';
The code to do this is below:
function naturalSort(a, b) // Function to natural-case insensitive sort multidimensional arrays by second index
{
// setup temp-scope variables for comparison evauluation
var re = /(-?[0-9\.]+)/g,
x = a[1].toString().toLowerCase() || '',
y = b[1].toString().toLowerCase() || '',
nC = String.fromCharCode(0),
xN = x.replace( re, nC + '$1' + nC ).split(nC),
yN = y.replace( re, nC + '$1' + nC ).split(nC),
xD = (new Date(x)).getTime(),
yD = xD ? (new Date(y)).getTime() : null;
// natural sorting of dates
if ( yD )
if ( xD < yD ) return -1;
else if ( xD > yD ) return 1;
// natural sorting through split numeric strings and default strings
for( var cLoc = 0, numS = Math.max(xN.length, yN.length); cLoc < numS; cLoc++ ) {
oFxNcL = parseFloat(xN[cLoc]) || xN[cLoc];
oFyNcL = parseFloat(yN[cLoc]) || yN[cLoc];
if (oFxNcL < oFyNcL) return -1;
else if (oFxNcL > oFyNcL) return 1;
}
return 0;
}
// Set values for index
var users = Array();
var temp = Array();
users.push(Array('72', 'testuser4'));
users.push(Array('91', 'testuser2'));
users.push(Array('12', 'testuser8'));
users.push(Array('3', 'testuser1'));
users.push(Array('18', 'testuser7'));
users.push(Array('47', 'testuser3'));
users.push(Array('16', 'testuser6'));
users.push(Array('20', 'testuser5'));
// Sort the array
var users_sorted = Array();
users_sorted = users.sort(naturalSort);
I'd use map once to make a new array of users,
then a second time to return the string you want from the new array.
var users= [];
users[562]= 'testuser3';
users[16]= 'testuser6';
users[834]= 'testuser1';
users[823]= 'testuser4';
users[23]= 'testuser2';
users[917]= 'testuser5';
var u2= [];
users.map(function(itm, i){
if(itm){
var n= parseInt(itm.substring(8), 10);
u2[n]= i;
}
});
u2.map(function(itm, i){
return 'users['+itm+']= testuser'+i;
}).join('\n');
/*returned value: (String)
users[834]= testuser1
users[23]= testuser2
users[562]= testuser3
users[823]= testuser4
users[917]= testuser5
users[16]= testuser6
*/
If you want to avoid any gaps. use a simple filter on the output-
u2.map(function(itm, i){
return 'users['+itm+']= testuser'+i;
}).filter(function(itm){return itm}).join('\n');
Sparse arrays usually spell trouble. You're better off saving key-value pairs in an array as objects (this technique is also valid JSON):
users = [{
"562": "testuser3"
},{
"16": "testuser6"
}, {
"834": "testuser1"
}, {
"823": "testuser4"
}, {
"23": "testuser2"
}, {
"917": "testuser5"
}];
As suggested, you can use a for loop to map the sorting function onto the array.
Array.prototype.sort() takes an optional custom comparison function -- so if you dump all of your users into an array in this manner [ [562, "testuser3"], [16, "testuser6"] ... etc.]
Then sort this array with the following function:
function(comparatorA, comparatorB) {
var userA = comparatorA[1], userB = comparatorB[1]
if (userA > userB) return 1;
if (userA < userB) return -1;
if (userA === userB) return 0;
}
Then rebuild your users object. (Which will loose you your sorting.) Or, keep the data in the newly sorted array of arrays, if that will work for your application.
A oneliner with array of array as a result:
For sorting by Key.
let usersMap = users.map((item, i) => [i, item]).sort((a, b) => a[0] - b[0]);
For sorting by Value. (works with primitive types)
let usersMap = users.map((item, i) => [i, item]).sort((a, b) => a[1] - b[1]);