Related
I was wondering if any function exist that can do something like that
I already tried zipWith and some variations of the map, but no good results.
Only working solution to this is 2x ForEach, but I was wondering if I can minimalize this to one function
_.map( (array1, array2), function(knownValue, indexOfArray1, indexOfArray2) );
What I need is to increase array1 and array 2 indexes simultaneously (like: a1: 1, a2: 1; a1: 2, a2: 2; ...).
This arrays have also other types of values
#EDIT
I messed a little.
What i really wanted is to make this wrongly writed code to work
_.map( (array1, array2), (valueOfArray1, valueOfArray2), someFunction(knownValue, valueOfArray1, valueOfArray2) );
So my intention is:
For array1 and array2 elements, execute the function "someFunction", and in the next increment, use the next element of each array.
Like :
_.map( (array1, array2), (array1[0], array2[0]), someFunction(knownValue, array1[0], array2[0]) );
and next
_.map( (array1, array2), (array1[1], array2[1]), someFunction(knownValue, array1[1], array2[1]) );
but I want it more elegant :P
PS (Sorry for this mess)
Here is an example with flatMap:
const arr1 = [1,3,5];
const arr2 = [2,4,6];
console.log(arr1.flatMap((x,i) =>[x, arr2[i]]))
Since you already seem to be using lodash (or something similar) the .zip should work. The only pitfall is that .zip produces "pairs" with one from each original array IN a new array.
So when you map over the result from the .zip, the first argument is an array. See the example below and note that I'm destructuring the first argument with
function([a1_item, a2_item], indexForBoth) { .. rest of function here }
const a1 = ['a', 'b', 'c'];
const a2 = ['One', 'Two', 'Three'];
const result = _.zip(a1, a2).map(function([a1_item, a2_item], indexForBoth) {
return a1_item + a2_item + indexForBoth;
});
console.log("a1", a1);
console.log("a2", a2);
console.log("a1 zipped with a2", _.zip(a1, a2) );
console.log("Result after mapping and concatinating", result);
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.11/lodash.min.js"></script>
Since they both have the same number of items, you can loop through array1 using map. The second parameter of map callback is the index. You can use it to get the equivalent value from array2 using array2[index]
array1.map((value1, index) => someFunction(knownValue, value1, array2[index]))
This creates an array of values returned by someFunction for each index
Here's a snippet with some sample data. Here, someFunction concatenates each value with a _ seperator
const someFunction = (...values) => values.join('_')
const array1 = [1, 2, 3],
array2 = [10, 20, 30],
knownValue = "fixed";
const output = array1.map((value1, i) => someFunction(knownValue, value1, array2[i]))
console.log(output)
This question already has answers here:
Using spread operator multiple times in javascript?
(4 answers)
Closed 4 years ago.
Here is what I tried:
let a = [[1,2], [3,4]];
a.map(val => ...val)
// => SyntaxError: expected expression, got '...'
// Expected output: [1,2,3,4]
I tried with explicit return statement and surrounding value with parenthesis but none worked...
I just wonder if there is a simple way to return "spreaded array" ?
Edit: Now I have seen this SO question which has got precision on how spread operator works although in doesn't really answer the question on how to "flatten" an array (I modified the title of the question).
This isn't valid syntax, for this to work you need to spread ("unpack" the contents of) your array into a container of some sort (such as an array). However, if you were to do something like:
a.map(val => [...val])
you would not be doing much with your array, and instead, you would end up with copies of the same array. Thus, you can use different methods other than .map such as .reduce or .flatMap/.flat to achieve your desired output:
Using .reduce with the spread syntax:
let a = [[1,2], [3,4]];
let res = a.reduce((acc, val) => [...acc, ...val], []);
console.log(res)
Using .flatMap():
let a = [[1,2], [3,4]];
let res = a.flatMap(val => val); // map the contents (without the holding array) [[1, 2], [3, 4]] -> [1, 2, [3, 4]] -> [1, 2, 3, 4]
console.log(res)
.flatMap() is, however, useless here, and thus using .flat() would simply suffice:
let a = [[1,2], [3,4]];
let res = a.flat();
console.log(res)
Try to use a.flatMap(x=>x); to flat array and map elements or flat (with no mapping)
a.flat();
let a = [[1,2], [3,4]];
let r=a.flat();
console.log(r);
In flat arg you can set deep-level of flatting - setting Infinity will flat any nested array
let a = [[1,2,[3,4,[5,[6]]]], [[7,[8]],9]];
let r=a.flat(Infinity);
console.log(r);
As written in comments, functions can only return one value.
But there is a simple trick what you can use:
let a = [[1,2], [3,4]];
a.reduce((a,b) => a.concat(b),[])
// Expected output: [1,2,3,4]
In JavaScript, how can I repeat an array which contains multiple elements, in a concise manner?
In Ruby, you could do
irb(main):001:0> ["a", "b", "c"] * 3
=> ["a", "b", "c", "a", "b", "c", "a", "b", "c"]
I looked up the lodash library, and didn't find anything that was directly applicable. Feature request: repeat arrays. is a feature request for adding it to lodash, and the best workaround given there is
const arrayToRepeat = [1, 2, 3];
const numberOfRepeats = 3;
const repeatedArray = _.flatten(_.times(numberOfRepeats, _.constant(arrayToRepeat)));
The questions Most efficient way to create a zero filled JavaScript array? and Create an array with same element repeated multiple times focus on repeating just a single element multiple times, whereas I want to repeat an array which has multiple elements.
Using reasonably well-maintained libraries is acceptable.
No need for any library, you can use Array.from to create an array of arrays you want repeated, and then flatten using [].concat and spread:
const makeRepeated = (arr, repeats) =>
[].concat(...Array.from({ length: repeats }, () => arr));
console.log(makeRepeated([1, 2, 3], 2));
On newer browsers, you can use Array.prototype.flat instead of [].concat(...:
const makeRepeated = (arr, repeats) =>
Array.from({ length: repeats }, () => arr).flat();
console.log(makeRepeated([1, 2, 3], 2));
You can use the Array constructor along with its fill method to fill it a number of times of the array you want to repeat, then concat them (the subarrays) into a single array:
const repeatedArray = [].concat(...Array(num).fill(arr));
Note: On older browsers (pre-ES6), you can use Function#apply to mimic the rest syntax above (concat will be called with each of the sub arrays passed to it as argument):
var repeatedArray = [].concat.apply([], Array(num).fill(arr));
Example:
const arrayToRepeat = [1, 2, 3];
const numberOfRepeats = 3;
const repeatedArray = [].concat(...Array(numberOfRepeats).fill(arrayToRepeat));
console.log(repeatedArray);
const repeat = (a, n) => Array(n).fill(a).flat(1)
console.log( repeat([1, 2], 3) )
Recursive alternative:
const repeat = (a, n) => n ? a.concat(repeat(a, --n)) : [];
console.log( repeat([1, 2], 3) )
My first idea would be creating a function like this
let repeat = (array, numberOfTimes) => Array(numberOfTimes).fill(array).reduce((a, b) => [...a, ...b], [])
console.log(repeat(["a", "b", "c"], 3))
using the fill method and reduce
Ideally, instead of using reduce you could use flatten but there's yet no support in browsers
Try
Array(3).fill(["a", "b", "c"]).flat()
console.log( Array(3).fill(["a", "b", "c"]).flat() );
Unfortunately, it is not possible natively in JS (Also operator overloading is not possible, so we can not use something like Array.prototype.__mul__), but we can create an Array with the proper target length, fill with placeholders, then re-map the values:
const seqFill = (filler, multiplier) =>
Array(filler.length * multiplier)
.fill(1)
.map(
(_, i) => filler[i % filler.length]
);
console.log(seqFill([1,2,3], 3));
console.log(seqFill(['a','b','c', 'd'], 5));
Or another way by hooking into the Array prototype, you could use the syntax of Array#seqFill(multiplier), this is probably the closest you can get to ruby syntax (rb can do basically everything with operator overloading, but JS can't):
Object.defineProperty(Array.prototype, 'seqFill', {
enumerable: false,
value: function(multiplier) {
return Array(this.length * multiplier).fill(1).map((_, i) => this[i % this.length]);
}
});
console.log([1,2,3].seqFill(3));
Apart from the obvious [].concat + Array.from({length: 3}, …)/fill() solution, using generators will lead to elegant code:
function* concat(iterable) {
for (const x of iterable)
for (const y of x)
yield y;
}
function* repeat(n, x) {
while (n-- > 0)
yield x;
}
const repeatedArray = Array.from(concat(repeat(3, [1, 2, 3])));
You can also shorten it to
function* concatRepeat(n, x) {
while (n-- > 0)
yield* x;
}
const repeatedArray = Array.from(concatRepeat(3, [1, 2, 3]));
Though other methods works simply, these too.
Array.fill() and Array.from() in previous methods will not work in IE. MDN Docs for Reference
Mehtod 1 : Loop and push (Array.prototype.push) the same into the array.
function mutateArray(arr,n)
{
var temp = [];
while(n--) Array.prototype.push.apply(temp,arr);
return temp;
}
var a = [1,2,3,4,5];
console.log(mutateArray(a,3));
Method 2: Join the array elements and String.repeat() to mutate the string and return the split string.
Note: The repeat method is not supported yet in IE and Android webviews.
function mutateArray(arr,n)
{
var arr = (arr.join("$")+"$").repeat(n).split("$");
arr.pop(); //To remove the last empty element
return arr;
}
var a = [1,2,3,4,5];
console.log(mutateArray(a,3));
I have below array format and i want to make union of it using lodash or normal js.
var testArray = [[1,2,3,4,5,6,7,8],[1,2,3,4,5,10,7,8],[1,2,3,6,7,8],[9],[3,4,5]]
I want to make union of all these into one and output should be below array.
testArray = [1,2,3,4,5,6,7,8,9,10]
You could combine flattenDeep with _.union. If needed apply sorting
var testArray = [[1,2,3,4,5,6,7,8],[1,2,3,4,5,10,7,8],[1,2,3,6,7,8],[9],[3,4,5]],
result = _.chain(testArray)
.flattenDeep()
.union();
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.15.0/lodash.min.js"></script>
Apply _.union() to the parent array:
var testArray = [[1,2,3,4,5,6,7,8],[1,2,3,4,5,10,7,8],[1,2,3,6,7,8],[9],[3,4,5]];
var result = _.union.apply(_, testArray);
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.2/lodash.min.js"></script>
Or use array spread if ES6 is supported:
const testArray = [[1,2,3,4,5,6,7,8],[1,2,3,4,5,10,7,8],[1,2,3,6,7,8],[9],[3,4,5]];
const result = _.union(...testArray);
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.2/lodash.min.js"></script>
With ES6 you can do this using spread syntax ... and Set
var testArray = [[1,2,3,4,5,6,7,8],[1,2,3,4,5,10,7,8],[1,2,3,6,7,8],[9],[3,4,5]]
var result = [...new Set([].concat(...testArray))];
console.log(result)
You can just call _.union.apply(null, arrays). It is documented here:
https://lodash.com/docs/#union
> var testArray = [[1,2,3,4,5,6,7,8],[1,2,3,4,5,10,7,8],[1,2,3,6,7,8],[9],[3,4,5]]
> lodash.union.apply(null, testArray)
[ 1, 2, 3, 4, 5, 6, 7, 8, 10, 9 ]
The apply trick is to transform your array of array in to function call arguments. If you need it sorted as well, you can just tuck .sort() at the end of it.
In just ES5 if you need that:
var flattened = Object.keys(testArray.reduce(function(acc, cur) {
cur.forEach(function(v) {acc[v] = true;});
return acc;
}, {})).sort(function(a, b) {return a - b;});
Say you have an array-like Javascript ES6 Iterable that you know in advance will be finite in length, what's the best way to convert that to a Javascript Array?
The reason for doing so is that many js libraries such as underscore and lodash only support Arrays, so if you wish to use any of their functions on an Iterable, it must first be converted to an Array.
In python you can just use the list() function. Is there an equivalent in ES6?
You can use Array.from or spread syntax (...).
Example:
const x = new Set([ 1, 2, 3, 4 ]);
const y = Array.from(x);
console.log(y); // = [ 1, 2, 3, 4 ]
const z = [ ...x ];
console.log(z); // = [ 1, 2, 3, 4 ]
Summary:
Array.from() function, it takes an iterable as in input and returns an array of the iterable.
Spread syntax: ... in combination with an array literal.
const map = new Map([[ 1, 'one' ],[ 2, 'two' ]]);
const newArr1 = [ ...map ]; // create an Array literal and use the spread syntax on it
const newArr2 = Array.from( map ); //
console.log(newArr1, newArr2);
Caveat when copying arrays:
Be cognizant of the fact that via these methods above only a shallow copy is created when we want to copy an array. An example will clarify the potential issue:
let arr = [1, 2, ['a', 'b']];
let newArr = [ ...arr ];
console.log(newArr);
arr[2][0] = 'change';
console.log(newArr);
Here because of the nested array the reference is copied and no new array is created. Therefore if we mutate the inner array of the old array, this change will be reflected in the new array (because they refer to the same array, the reference was copied).
Solution for caveat:
We can resolve the issue of having shallow copies by creating a deep clone of the array using JSON.parse(JSON.stringify(array)). For example:
let arr = [1, 2, ['a', 'b']]
let newArr = Array.from(arr);
let deepCloneArr = JSON.parse(JSON.stringify(arr));
arr[2][0] = 'change';
console.log(newArr, deepCloneArr)
You can use the Array.from method, which is being added in ES6, but only supports arrays and iterable objects like Maps and Sets (also coming in ES6). For regular objects, you can use Underscore's toArray method or lodash's toArray method, since both libraries actually have great support for objects, not just arrays. If you are already using underscore or lodash, then luckily they can handle the problem for you, alongside adding various functional concepts like map and reduce for your objects.
The following approach is tested for Maps:
const MyMap = new Map([
['a', 1],
['b', 2],
['c', 3]
]);
const MyArray = [...MyMap].map(item => {
return {[item[0]]: item[1]}
});
console.info( MyArray ); //[{"a", 1}, {"b", 2}, {"c": 3}]
<Your_Array> = [].concat.apply([], Array.from( <Your_IterableIterator> ));
You could also do the following, but both approaches are certainly not recommendable (merely a proof-of-concept for completeness):
let arr = [];
for (let elem of gen(...)){
arr.push(elem);
}
Or "the hard way" using ES5 + generator function (Fiddle works in current Firefox):
var squares = function* (n) {
for (var i = 0; i < n; i++) {
yield i * i;
}
};
var arr = [];
var gen = squares(10);
var g;
while (true) {
g = gen.next();
if (g.done) {
break;
}
arr.push(g.value);
}