I'm new to JS and I have a task to flatten an array. What I have is [1,2,[3,4]] and I have to turn it into [1,2,3,4]. I use the following approach
function flatten(arr){
return arr.reduce(function(a, b) {
return a.concat(b);
});
}
But it says concat is not a function.
Array.isArray(arr); //returns true
Can someone tell me what to do here :)
Your arr value is [1,2,[3,4]]. That's an array with three entries: the number 1, the number 2, and the array [3,4].
The first two arguments to a reduce callback are
the accumulator (which is the return value of the previous invocation of the callback) and
a value taken in sequence from the array.
You don't supply an initial accumulator in your code, so that means the first call to the reduce callback accepts the first two entries as arguments (i.e., the first element of the array is used as an accumulator). concat is not a function because the initial value of a is a number, not an array. You tested to see if arr was an array, but did not test to see if a was an array.
The clear solution here is to supply an initial value to use as an accumulator:
function flatten(arr){
return arr.reduce(function(a, b) {
return a.concat(b);
}, []);
}
This creates an empty array (with []) and then supplies it as the a value for the first call to the reduce callback.
You can use this solution:
var array = [].concat.apply([], [1,2,[3,4]]);
console.log(array);
DOCS about the array says
The JavaScript Array object is a global object that is used in the construction of arrays; which are high-level, list-like objects.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array
In your example for reduce you will have to provide an initial value which can b an empty array.
var flattened = [1,2,[3,4]].reduce(function(a, b) {
return a.concat(b);
}, []);
console.log(flattened)
You just need to provide array [] after the callback function as initial value to reduce, otherwise that value will be first element of your array. So in your code in first iteration of reduce you are trying to do this a.concat(b) where a is 1 and b is 2 and that throws the error.
function flatten(arr) {
return arr.reduce(function(a, b) {
return a.concat(b);
}, []);
}
console.log(flatten([1, 2, [3, 4]]))
concat is expecting an Array of values to join with another Array. The reason this is failing is because your Array isn't a full matrix.
The problem with your array is that it wouldn't be wise to create a single hard coded solution for that specific Array, you should probably create a recursive function like:
function flattenArr(arr) {
const newArr = []
function recursiveFlat(a) {
if(!Array.isArray(a)) {
newArr.push(a);
}
else {
for (let i = 0; i < a.length; i++) {
if(Array.isArray(a[i])) {
recursiveFlat(a[i]);
}
else {
newArr.push(a[i]);
}
}
}
}
arr.forEach(item => recursiveFlat(item));
return newArr;
}
arr = [1, 2, [3, 4]]
console.log(flattenArr(arr))
That way it will always flatten the array no matter the shape.
You just make sure that arr is an array. But,you call concat on a.
test this:
if(a instanceof Array)
You can use Array.prototype.concat() combined with Spread syntax:
var flattened = [].concat.call([], ...[1, 2, [3, 4]]);
console.log(flattened);
Related
I am studying now at FreeCodeCamp, and here is a challenge:
"We have defined a function, copyMachine which takes arr (an array)
and num (a number) as arguments. The function is supposed to return a
new array made up of num copies of arr. We have done most of the work
for you, but it doesn't work quite right yet. Modify the function
using spread syntax so that it works correctly (hint: another method
we have already covered might come in handy here!)."
And here is a solution:
function copyMachine(arr, num) {
let newArr = [];
while (num >= 1) {
// Only change code below this line
newArr.push([...arr])
// Only change code above this line
num--;
}
return newArr;
}
console.log(copyMachine([true, false, true], 2));
What the point to add extra brackets and dots (...) if i could just add newArr.push(arr) instead and get the same result?
The result is: [ [ true, false, true ], [ true, false, true ] ]
It's the matter of reference.
When using this syntax newArr.push(arr), you're pushing the original array from the argument, so whenever the arr changes its content, arrays inside newArr will also update since it is always the same one array.
When using spread syntax, you're actually pushing a copy of that arr. This mean it's a new array that is not tied to the array you pass to a function
Consider this
function copyMachine(arr, num) {
let newArr = [];
while (num >= 1) {
// Only change code below this line
newArr.push(arr)
// Only change code above this line
num--;
}
return newArr;
}
const oldArr = [true, false, true];
const newArr = copyMachine(oldArr, 2);
oldArr.push(true);
console.log(newArr); // contains an element added to the old array
Spread syntax (...) allows an iterable such as an array expression or string to be expanded in places where zero or more arguments (for function calls) or elements (for array literals) are expected, or an object expression to be expanded in places where zero or more key-value pairs (for object literals) are expected.
In the provided code the spread operator is used to make a new array using the elements of the array named arr and then push it onto newArr.
This is similar to doing newArr.push(arr);
I am trying to compare two given parameters of a function. The exact problem is as follows:
You will be provided with an initial array (the first argument in the destroyer function), followed by one or more arguments. Remove all elements from the initial array that are of the same value as these arguments.
Note
You have to use the arguments object.
console.log(destroyer([1, 2, 3, 1, 2, 3], 2, 3)); // expected output: [1,1]
I am using filter method to iterate over the array but I couldn't compare the args with the elements of the array inside the callback of the filter.
function destroyer(arr, ...args) {
let result = arr.filter(num => {
for (let i = 0; i<=args.length; i++ ){
num !== args[i]
}
});
return result;
}
console.log(destroyer([1, 2, 3, 1, 2, 3], 2, 3));
I can iterate with for loop but I cannot use the output of for loop to do filter iteration.
Any ideas?
Probably an easier way to achieve the goal using .filter() with .includes(). Additionally you can use ...rest so called rest parameters for you function, see form the documentation:
The rest parameter syntax allows us to represent an indefinite number of arguments as an array.
Try as the following:
const destroyer = (arr, ...rest) => {
return arr.filter(num => !rest.includes(num));
}
console.log(destroyer([1, 2, 3, 1, 2, 3], 2, 3));
I hope this helps!
The filter() method creates a new array with all elements that pass the test implemented by the provided function.
Example:
const words = ['spray', 'limit', 'elite', 'exuberant', 'destruction', 'present'];
const result = words.filter(word => word.length > 6);
console.log(result);
// expected output: Array ["exuberant", "destruction", "present"]
via MDN
Filter iterates over all elements of some array and returns a new array. It puts an element in the new array only if callback (your function invoked as a parameter of filter) return true otherwise it's omitted.
Next it's worth to use rest parameters to achieve two arrays (initial and values to exclude).
The rest parameter syntax allows us to represent an indefinite number of arguments as an array.
function sum(...theArgs) {
return theArgs.reduce((previous, current) => {
return previous + current;
});
}
console.log(sum(1, 2, 3));
// expected output: 6
console.log(sum(1, 2, 3, 4));
// expected output: 10
Solution with explanation:
//Declare your function, first parameter is initial array, the second one is also array created by using rest parameters
function destroyer(initialArray = [], ...toExclude) {
// filter initialArray, if el (single element) is NOT included in "toExclude" it returns true
// and add this particular element to the result array
let result = initialArray.filter(el => toExclude.includes(el) == false);
//return result
return result;
}
I am performing an Array.sort with the compare method like so:
orderNotes(notes){
function compare(a, b) {
const noteA =a.updatedAt
const noteB =b.updatedAt
let comparison = 0;
if (noteA < noteB) {
comparison = 1;
} else if (noteA > noteB) {
comparison = -1;
}
return comparison;
}
return notes.sort(compare)
}
Now since I need to sort the array anyway and loop through each element with the Array.sort, I want to use this chance to check if the note.id matches on the neighboring note, and remove the duplicate from the array (doesn't matter which one). This will save me the trouble to loop again just to check duplication.
Is it possible to alter the array inside the compare() function and remove the duplicate?
Best
Is it possible to alter the array inside the compare() function and remove the duplicate?
You could .splice(...) the element out of it if it doesn't match, but actually this:
This will save me the trouble to loop again just to check duplication.
Is a missconception. Looping an array and doing two tasks will only be slightly faster than two loops, as only the looping part gets duplicated, not the tasks done in the loop. Therefore just:
const ids = new Set;
const result = array
.filter(it => ids.has(it.id) && ids.add(it.id))
.sort((a, b) => a.updatedAt - b.updatedAt);
It might be a simpler solution to use Array.prototype.reduce to remove the duplicates in an additional step instead of while sorting:
//Starting with your ordered array
var ordered = [1, 1, 2, 3, 3, 3, 4, 5, 6, 7, 7, 8, 9, 9, 9];
//Now create a new array excluding the duplicates
var orderedAndUnique = ordered.reduce((accum, el) => {
if (accum.indexOf(el) == -1) {
accum.push(el);
return accum;
}
return accum;
}, []);
console.log(orderedAndUnique);
I put together a JavaScript function that is supposed to flatten a nested array. However, this method always just returns the original array. For example, running this function with the following array [1, 2, 3, [4, 5, [6], [ ] ] ] will just return that array. I know there are ways to do this with reduce, but what logical reason is preventing this method from working? .map should allow me to manipulate a return value and return that within the new array through the recursion call.
function mapper(array) {
return array.map((item) => {
return (Array.isArray(item)) ? mapper(item) : item
}
)}
what logical reason is preventing this method from working?
var m = [1, 2, 3, [4, 5, [6], []]];
function mapper(array) {
return array.map((item) => {
// for 1,2,3 it will return item
// when it sees an array it will again call mapper & map
// function will return a new array from it, so map on
// [4, 5, [6], []] will return a new array but will not take out
// individual element and will put it in previous array
return (Array.isArray(item)) ? mapper(item) : item
}
)}
mapper(m)
map function does not mutate the original array but it will return a new array.
You are mapping the array to itself. Basically because map will return an array with exact same number of elements as the input. You cant expect it to return more, so you cant use it for flattening the array.
Should use reduce instead:
function flatten(obj) {
if (Array.isArray(obj)) {
return obj.reduce((a, b) => a.concat(flatten(b)), []);
} else {
return [obj];
}
}
Let's say I'm given an array. The length of this array is 3, and has 3 elements:
var array = ['1','2','3'];
Eventually I will need to check if this array is equal to an array with the same elements, but just twice now. My new array is:
var newArray = ['1','2','3','1','2','3'];
I know I can use array.splice() to duplicate an array, but how can I duplicate it an unknown amount of times? Basically what I want is something that would have the effect of
var dupeArray = array*2;
const duplicateArr = (arr, times) =>
Array(times)
.fill([...arr])
.reduce((a, b) => a.concat(b));
This should work. It creates a new array with a size of how many times you want to duplicate it. It fills it with copies of the array. Then it uses reduce to join all the arrays into a single array.
The simplest solution is often the best one:
function replicate(arr, times) {
var al = arr.length,
rl = al*times,
res = new Array(rl);
for (var i=0; i<rl; i++)
res[i] = arr[i % al];
return res;
}
(or use nested loops such as #UsamaNorman).
However, if you want to be clever, you also can repeatedly concat the array to itself:
function replicate(arr, times) {
for (var parts = []; times > 0; times >>= 1) {
if (times & 1)
parts.push(arr);
arr = arr.concat(arr);
}
return Array.prototype.concat.apply([], parts);
}
Basic but worked for me.
var num = 2;
while(num>0){
array = array.concat(array);
num--}
Here's a fairly concise, non-recursive way of replicating an array an arbitrary number of times:
function replicateArray(array, n) {
// Create an array of size "n" with undefined values
var arrays = Array.apply(null, new Array(n));
// Replace each "undefined" with our array, resulting in an array of n copies of our array
arrays = arrays.map(function() { return array });
// Flatten our array of arrays
return [].concat.apply([], arrays);
}
console.log(replicateArray([1,2,3],4)); // output: [1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3]
What's going on?
The first two lines use apply and map to create an array of "n" copies of your array.
The last line uses apply to flatten our recently generated array of arrays.
Seriously though, what's going on?
If you haven't used apply or map, the code might be confusing.
The first piece of magic sauce here is the use of apply() which makes it possible to either pass an array to a function as though it were a parameter list.
Apply uses three pieces of information: x.apply(y,z)
x is the function being called
y is the object that the function is being called on (if null, it uses global)
z is the parameter list
Put in terms of code, it translates to: y.x(z[0], z[1], z[2],...)
For example
var arrays = Array.apply(null, new Array(n));
is the same as writing
var arrays = Array(undefined,undefined,undefined,... /*Repeat N Times*/);
The second piece of magic is the use of map() which calls a function for each element of an array and creates a list of return values.
This uses two pieces of information: x.map(y)
x is an array
y is a function to be invoked on each element of the array
For example
var returnArray = [1,2,3].map(function(x) {return x + 1;});
would create the array [2,3,4]
In our case we passed in a function which always returns a static value (the array we want to duplicate) which means the result of this map is a list of n copies of our array.
You can do:
var array = ['1','2','3'];
function nplicate(times, array){
//Times = 2, then concat 1 time to duplicate. Times = 3, then concat 2 times for duplicate. Etc.
times = times -1;
var result = array;
while(times > 0){
result = result.concat(array);
times--;
}
return result;
}
console.log(nplicate(2,array));
You concat the same array n times.
Use concat function and some logic: http://www.w3schools.com/jsref/jsref_concat_array.asp
Keep it short and sweet
function repeat(a, n, r) {
return !n ? r : repeat(a, --n, (r||[]).concat(a));
}
console.log(repeat([1,2,3], 4)); // [1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3]
http://jsfiddle.net/fLo3uubk/
if you are inside a loop you can verify the current loop index with the array length and then multiply it's content.
let arr = [1, 2, 3];
if(currentIndex > arr.length){
//if your using a loop, make sure to keep arr at a level that it won't reset each loop
arr.push(...arr);
}
Full Example:
https://jsfiddle.net/5k28yq0L/
I think you will have to write your own function, try this:
function dupArray(var n,var arr){
var newArr=[];
for(var j=0;j<n;j++)
for(var i=0;i<arr.length;i++){
newArr.push(arr[i]);
}
return newArr;
}
A rather crude solution for checking that it duplicates...
You could check for a variation of the length using modulus:
Then if it might be, loop over the contents and compare each value until done. If at any point it doesn't match before ending, then it either didn't repeat or stopped repeating before the end.
if (array2.length % array1.length == 0){
// It might be a dupe
for (var i in array2){
if (i != array1[array2.length % indexOf(i)]) { // Not Repeating }
}
}