Related
So I have my function uniteUnique. It should get the arguments and concatonate them to a single array. If there would be a specific amount of arguments for example 3, i would implement it like the function bellow
function uniteUnique(arr) {
var args = [];
var newArgs;
for (var i = 0; i < arguments.length; i++) {
args.push(arguments[i]);
newArgs = args[0].concat(args[1], args[2]);
}
return newArgs ;
}
uniteUnique([1, 3, 2], [1, [5]], [2, [4]]);
But what if uniteUnique function would have 2 arguments or any other number.
uniteUnique([1, 2, 3], [5, 2, 1])
How to make my function know how many arguments it should concatonate, and how would it be implemented inside the concat() function ?
EDIT:
Output should look like this:
uniteUnique([1, 3, 2], [1, [5]], [2, [4]]) should return [1,3,1,1,[5],2,[4]]and uniteUnique([1, 2, 3], [5, 2, 1]) should return [1,2,3,5,2,1]
I'm not sure if I fully understand your question, but: what about this simple solution?
function uniteUnique() {
return Array.prototype.slice.call(arguments);
}
The arguments object is not really an Array instance, and does not have any of the Array methods. So, arguments.slice(...) will not work because the arguments object does not have the slice method.
Instead, Arrays do have this method, and because the arguments object is very similar (...) to an array, the two are compatible. This means that we can use array methods with the arguments object. And array methods will return arrays rather than other argument objects.
For a more throughtful explanation please see this SO answer ...
UPDATE (to answer OP comment):
If you need deep merging, you can do:
function uniteUnique() {
return Array.prototype.concat.apply([], arrays);
}
or even:
function uniteUnique() {
return [].concat.apply([], arrays);
}
This should work since the dafaule value of Symbol.isConcatSpreadable is false, so concat() acts deeply...
According to your examples you want to flatten arguments array. In ES6 you can use Rest parameters to get arguments array and Spread syntax to flatten it:
function uniteUnique(...args) {
return [].concat(...args);
}
https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/arguments
You can access the arguments variable to loop through all arguments passed to a given function.
Was able to solve this like that,
function uniteUnique(arr) {
var myNewArray = [].concat.apply([], Array.prototype.slice.call(arguments));
return myNewArray;
}
uniteUnique([1, 3, 2], [1, [5]], [2, [4]]) ;
I'm trying to solve a freeCodeCamp exercise with this goal:
Write a function that takes two or more arrays and returns a new array
of unique values in the order of the original provided arrays.
In other words, all values present from all arrays should be included
in their original order, but with no duplicates in the final array.
The unique numbers should be sorted by their original order, but the
final array should not be sorted in numerical order.
So what I do is concatenate all the arguments into a single array called everything. I then search the array for duplicates, then search the arguments for these duplicates and .splice() them out.
So far everything works as expected, but the last number of the last argument does not get removed and I can't really figure out why.
Can anybody please point out what I'm doing wrong? Please keep in mind that I'm trying to learn, so obvious things probably won't be obvious to me and need to be pointed out. Thanks in advance.
function unite(arr1, arr2, arr3) {
var everything = [];
//concat all arrays except the first one
for(var x = 0; x < arguments.length; x++) {
for(var y = 0; y < arguments[x].length; y++) {
everything.push(arguments[x][y]);
}
}
//function that returns duplicates
function returnUnique(arr) {
return arr.reduce(function(dupes, val, i) {
if (arr.indexOf(val) !== i && dupes.indexOf(val) === -1) {
dupes.push(val);
}
return dupes;
}, []);
}
//return duplicates
var dupes = returnUnique(everything);
//remove duplicates from all arguments except the first one
for(var n = 1; n < arguments.length; n++) {
for(var m = 0; m < dupes.length; m++) {
if(arguments[n].hasOwnProperty(dupes[m])) {
arguments[n].splice(arguments[n].indexOf(dupes[m]), 1);
}
}
}
//return concatenation of the reduced arguments
return arr1.concat(arr2).concat(arr3);
}
//this returns [1, 3, 2, 5, 4, 2]
unite([1, 3, 2], [5, 2, 1, 4], [2, 1]);
Looks like you overcomplicated it a bit ;)
function unite() {
return [].concat.apply([], arguments).filter(function(elem, index, self) {
return self.indexOf(elem) === index;
});
}
res = unite([1, 2, 3], [5, 2, 1, 4], [2, 1], [6, 7, 8]);
document.write('<pre>'+JSON.stringify(res));
Explanations
We split the problem into two steps:
combine arguments into one big array
remove non-unique elements from this big array
This part handles the first step:
[].concat.apply([], arguments)
The built-in method someArray.concat(array1, array2 etc) appends given arrays to the target. For example,
[1,2,3].concat([4,5],[6],[7,8]) == [1,2,3,4,5,6,7,8]
If our function had fixed arguments, we could call concat directly:
function unite(array1, array2, array3) {
var combined = [].concat(array1, array2, array3);
// or
var combined = array1.concat(array2, array3);
but as we don't know how many args we're going to receive, we have to use apply.
someFunction.apply(thisObject, [arg1, arg2, etc])
is the same as
thisObject.someFunction(arg1, arg2, etc)
so the above line
var combined = [].concat(array1, array2, array3);
can be written as
var combined = concat.apply([], [array1, array2, array3]);
or simply
var combined = concat.apply([], arguments);
where arguments is a special array-like object that contains all function arguments (actual parameters).
Actually, last two lines are not going to work, because concat isn't a plain function, it's a method of Array objects and therefore a member of Array.prototype structure. We have to tell the JS engine where to find concat. We can use Array.prototype directly:
var combined = Array.prototype.concat.apply([], arguments);
or create a new, unrelated, array object and pull concat from there:
var combined = [].concat.apply([], arguments);
This prototype method is slightly more efficient (since we're not creating a dummy object), but also more verbose.
Anyways, the first step is now complete. To eliminate duplicates, we use the following method:
combined.filter(function(elem, index) {
return combined.indexOf(elem) === index;
})
For explanations and alternatives see this post.
Finally, we get rid of the temporary variable (combined) and chain "combine" and "dedupe" calls together:
return [].concat.apply([], arguments).filter(function(elem, index, self) {
return self.indexOf(elem) === index;
});
using the 3rd argument ("this array") of filter because we don't have a variable anymore.
Simple, isn't it? ;) Let us know if you have questions.
Finally, a small exercise if you're interested:
Write combine and dedupe as separate functions. Create a function compose that takes two functions a and b and returns a new function that runs these functions in reverse order, so that compose(a,b)(argument) will be the same as b(a(argument)). Replace the above definition of unite with unite = compose(combine, dedupe) and make sure it works exactly the same.
You can also try this :
var Data = [[1, 2, 3], [5, 2, 1, 4], [2, 1], [6, 7, 8]]
var UniqueValues = []
for (var i = 0; i < Data.length; i++) {
UniqueValues = [...new Set(UniqueValues.concat(Data[i]))]
}
console.log(UniqueValues)
Right now I have an array of the form [[1, 2], [3, 4], ...] and need to use an array of the keys [1, 3, ...] and was wondering if there was a javascript or d3 library function that took in the array of arrays and a function, then returned a new array according to the function. Something like this:
var data = [[1, 2], [3, 4]]
var keyArray = d3.transformArray(data, function(d) { return d[0]});
// keyArray = [1, 3]
So I can avoid looping over the data array again
var keyArray = [];
for (i = 0; i < data.length; i += 1) {
keyArray.push(data[i][0]);
}
// keyArray[1, 3]
This seems like a common enough thing to do using d3, but I wasn't sure if there's a specific name for this process of using a specific object and a function to create a new object of the same type.
you can use Array.prototype.map
x = [1,2,3].map(function(item){return item+1;});
this will result int [2,3,4]
read about this here https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/map
I have a JSON response like this:
{"result":[["abc","de"],["fgh"],["ij","kl"]]}
I want the response to be in the form:
{"result":["abc","de","fgh","ij","kl"]}
How can I achieve this?
From the mozilla docs
var flattened = [[0, 1], [2, 3], [4, 5]].reduce(function(a, b) {
return a.concat(b);
});
// flattened is [0, 1, 2, 3, 4, 5]
var test={"result":[["abc","de"],["fgh"],["ij","kl"]]};
var tmp=[];
for(var i in test.result){
for(var j in test.result[i]){
tmp.push(test.result[i][j]);
}
}
test.result=tmp;
alert(JSON.stringify(test));
jsfiddle link http://jsfiddle.net/fu26849m/
jsFiddle
var arrayToFlatten = [[0, 1], [2, 3], [4, 5]];
Native (from Merge/flatten an array of arrays in JavaScript?):
var flattenedNative = arrayToFlatten.reduce(function(a, b) {
return a.concat(b);
});
alert(flattenedNative); // 0,1,2,3,4,5
jQuery (from How to flatten array in jQuery?):
var flattenedJQuery = $.map(arrayToFlatten, function(n) {
return n;
});
alert(flattenedJQuery); // 0,1,2,3,4,5
Native alternative (from Merge/flatten an array of arrays in JavaScript?):
var flattenedNativeAlt = [].concat.apply([], arrayToFlatten);
alert(flattenedNativeAlt); // 0,1,2,3,4,5
My first suggestion for this is you should create json directly as you want to use.
Do not modify it after you get.
You can also use this , this will give you value as you want.:
var mainText= JSON.parse('{"result":[["abc","de"],["fgh"],["ij","kl"]]}');
var arr = [];
for(var val1 in mainText.result)
{
arr = arr.concat(mainText.result[val1]);
}
mainText.result = arr;
console.log(JSON.stringify(mainText));
The reduce() and concat() functions can be combined to flatten an array:
var json = {"result":[["abc","de"],["fgh"],["ij","kl"]]};
function concatArrays(a, b) { return a.concat(b); }
json.result = json.result.reduce(concatArrays);
console.log(json); //{"result":["abc","de","fgh","ij","kl"]}
See it in action:
http://jsfiddle.net/cazomufn/
I like lodash' flatten (if you can live with another dependency.)
json.result = _.flatten(json.result);
// { result:['abc','de','fgh','ij','kl'] }
For example reduce isn't supported before IE9 but lodash would still work (compatibility build).
As a continuation of my min/max across an array of objects I was wondering about the performance comparisons of filter vs map.
So I put together a test on the values in my code as was going to look at the results in FireBug.
This is the code:
var _vec = this.vec;
min_x = Math.min.apply(Math, _vec.filter(function(el){ return el["x"]; }));
min_y = Math.min.apply(Math, _vec.map(function(el){ return el["x"]; }));
The mapped version returns the correct result. However the filtered version returns NaN. Breaking it out, stepping through and finally inspecting the results, it would appear that the inner function returns the x property of _vec but the actual array returned from filter is the unfiltered _vec.
I believe my usage of filter is correct - can anyone else see my problem?
Here's a simple test:
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>S:GTC Map Test</title>
</head>
<body>
<script type="text/javascript">
function vector(x,y,z) { this.x = x; this.y =y; this.z=z; }
var vec = [];
vec.push(new vector(1,1,1));
vec.push(new vector(2,2,2));
vec.push(new vector(2,3,3));
var _vec = vec;
min_x = Math.min.apply(Math, _vec.filter(function(el){ return el["x"]; }));
min_y = Math.min.apply(Math, _vec.map(function(el){ return el["x"]; }));
document.write("<br>filter = " + min_x);
document.write("<br>map = " + min_y);
</script>
</body>
</html>
No, the filter method doesn't return the unfiletered array. It returns an array containing the items where the inner function returns true.
As you are not returning a boolean value from the inner function, the value is converted to boolean, so the object reference is converted to true. Thus, it returns a new array that contains all the items from the original array.
The filter method doesn't do the same as the map method. The map method is used to convert each item of an array, while the filter method is used to select certain items of an array. Comparing the performance between the methods is moot, as only one of them does what you want to do.
Quoted from:
JavaScript: The Definitive Guide
by David Flanagan
map()
The map() method passes each element of the array on which it is
invoked to the function you specify, and returns an array containing
the values returned by that function.
For example:
a = [1, 2, 3];
b = a.map(function(x) { return x*x; }); // b is [1, 4, 9]
The function you pass to map() is invoked in the same way as a
function passed to forEach(). For the map() method, however, the
function you pass should return a value.Note that map() returns a new
array: it does not modify the array it is invoked on. If that array is
sparse, the returned array will be sparse in the same way: it will
have the same length and the same missing elements.
filter()
The method returns an array containing a subset of the elements of the
array on which it is invoked. The function you pass to it should be
predicate: a function that returns true or false. The predicate is
invoked just as for forEach() and map(). If the return value is true ,
or a value that converts to true, then the element passed to the
predicate is a member of the subset and is added to the array that
will become the return value.
Examples:
a = [5, 4, 3, 2, 1];
smallvalues = a.filter(function(x) { return x < 3 }); // [2, 1]
everyother = a.filter(function(x,i) { return i%2==0 }); // [5, 3, 1]
// MAP creates a new array
// MPA return new Array
var arr = [1, 2, 3, 4, 5, 6, 7];
var newArr = arr.map((el) => {
return el * 2;
});
console.log(newArr); //2,4,3,8,10,12,14
// filter() return new Array
var newFilter = arr.filter((el) => {
return el * 2;
});
console.log(newFilter); // 1,2,3,4,5,6,7
now you can see I have return el*2 both for map and filter they are giving a different output
Filter()
The filter() method creates a new array filled with all array elements that pass a test implemented by the function.
If this conditional returns true, the element gets pushed to the output array. If the condition returns false, the element does not get pushed to the output array.
var arr = [1, 2, 3, 4, 5, 6, 7];
var newFilter = arr.filter((el) => {
return el > 3;
});
console.log(newFilter); //[1, 2, 3, 4]
Map()
The map() method is used for creating a new array from an existing one, applying a function to each one of the elements of the first array
var arr = [1, 2, 3, 4, 5, 6, 7];
var newArr = arr.map((el) => {
return el * 2;
});
console.log(newArr); //2,4,3,8,10,12,14