Merge multiple arrays together if not null - javascript

I am building a React App and am using Async Actions to make request to outside API's and bring in data into my app. I have 4 arrays that I need to merge together. Since I am using Async Actions the default values if theres no data is null. I want to check to see if the array is not null and if it has a value then merge it with the others into a single array. Once I have all the data into a single array I am going to use the includes method to see if any of the values are present in the array. Using the spread operator I don't think is going to work here as it will fail as null is not an iterable. Is there a way to do this with reduce to check through each of the 4 separate arrays make sure the value is not null and then combine them together into one array.

Your question leads you to the answer. :)
Essentially you are asking how to filter all non-array inputs and then combine (or concatenate) them together into a new array.
A couple of notes on the following approach:
For better stability in filtering, rather than using a blacklist (not null), use a whitelist (Array.isArray) to ensure only arrays are combined.
The spread operator can be used to then create an arguments list for a new array's concat method.
const arr1 = [1,2];
const arr2 = null;
const arr3 = [3,4];
const arr4 = [5];
const concat = (...arrays) => [].concat(...arrays.filter(Array.isArray));
console.log(concat(arr1, arr2, arr3, arr4));
For a bit of fun, if the combined array needs to be unique values (assuming simple types for values) then including a quick cast to a Set and back to an Array can make that happen:
const arr1 = [1,2];
const arr2 = null;
const arr3 = [3,4];
const arr4 = [4,5];
const concat = (...arrays) =>[].concat(...arrays.filter(Array.isArray));
const unique = (array) => [...new Set(array)];
const concated = concat(arr1, arr2, arr3, arr4);
const uniqued = unique(concated);
console.log({concated, uniqued});

Here is a one line solution (ES6).
At the first part, we merge all arrays, and then filter array elements - we include only "not null" values and exclude duplicates:
const arr1 = [1, null, 6, 'q'],
arr2 = null,
arr3 = [1, 1, null, 1],
arr4 = ['e', 'q', 6, 1, null];
const final = []
.concat(arr1, arr2, arr3, arr4)
.filter((item, i, arr) => item && arr.indexOf(item) === i);
console.log(final); // Expected output: [1, 6, "q", "e"]

var a = [1, 2, 3]
var b = null
var c = [...a||[], ...b||[]]
console.log(c)

If you assign your arrays to properties of an object obj, you can simply iterate over the object :
const a = ["a", "1"], b = null, c = ["c", 78], d = []
const obj = { a, b, c, d }
let res = []
for(let key in obj) {
res = res.concat(Array.isArray(obj[key]) ? obj[key] : []);
}
console.log(res)

Related

Convert 2D nested array into 1D array in JavaScript?

Given a 2D array as shown in the example, how to manage to combine the string digits into one.
ex: Array2 = [[1,2,3],[4,5,6],[7,8,9]];
required sol:
Array1 = [123, 245, 789];
You can deconstruct your task into two separate problems, that we will solve in turn.
We need to take an array of numbers (e.g. [1, 2 ,3]) and join them together into a string (e.g. '123').
There is, in fact, a dedicated function for this in JavaScript - the join() function:
[1, 2, 3].join(''); // '123'
We need to perform the previous function on each object in an array, returning an array of the transformed objects.
JavaScript again has you covered, this time with the map() function:
[a, b, c].map((element) => foo(element)); // [foo(a), foo(b), foo(c)]
All we have to do now is join these too together, so that the operation we perform on each element of the parent array, is to join all of the child elements together:
[[1, 2, 3], [4, 5, 6], [7, 8, 9]].map((array) => array.join('')); // ['123', '456', '789']
function foo(arr){
let toReturn = [];
for(let i = 0;i < arr.length;i++){
toReturn.push(arr[i].join(""));
}
return(toReturn);
}
console.log(foo([[1,2,3],[4,5,6],[7,8,9]]));
You can use reduce to aggregate your data combined with .join('') to get all element items of each array item into a single value like below:
const data = [[1,2,3],[4,5,6],[7,8,9]];
const result = data.reduce((acc, curr) => {
acc.push(curr.join(''));
return acc;
}, []);
console.log(result);
Return all files as a 1D array from given path
const fetchAllFilesFromGivenFolder = (fullPath) => {
let files = [];
fs.readdirSync(fullPath).forEach(file => {
const absolutePath = path.join(fullPath, file);
if (fs.statSync(absolutePath).isDirectory()) {
const filesFromNestedFolder = fetchAllFilesFromGivenFolder(absolutePath);
filesFromNestedFolder.forEach(file => {
files.push(file);
})
}
else return files.push(absolutePath);
});
return files
}

React: Add values from array in another array

I'm trying to do this kind of operation but I don't understand how to do.
I have two array:
array1 = [elem1, elem2, elem3]
and a second array:
array2 = [elemA, elemB, elemC]
What i would to obtain is:
arrayFinal = [elem1:elemA, elem2:elemB, elem3:elemC]
How can I do??
Thank you so much.
EDIT:
the arrayFinal is not key:value, but the elem1:elemA is only the first value, elem2:elemB is the second value, for example using a join.
EDIT2:
I make an example of valure of array:
array1 = [0, 20, 40, 60, 80]
array2 = [-97:61:-1008; -97:60:-1008; -97:73:-1006, -98:70:-1008]
arrayFinal = [0:-96:61:-1009, 20:-97:61:-1008, 40:-97:60:-1008, 60:-97:73:-1006, 80:-98:70:-1008]
I See that there is a lot of confusion going on in the answers/comments, some people think you want to combine the arrays into key/value pairs, while others think you want to concat the arrays, while others think you want to make it an array where array1 is the index and array2 is the value at that index.
This is all caused by your formatting of [Elem1:ElemA, Elem2:ElemB]. which isnt even valid JS as far as i am aware.
However you edited your question, and now it seems like you want to combine them like [[Elem1, ElemA], [Elem2, ElemB]] in which case you can do that really easily in this way:
const array1 = ["elem1", "elem2", "elem3"];
const array2 = [1, 2, 3];
const result = array1.map((key, i) => [key, array2[i]]);
this will yield the result: [ [ 'elem1', 1 ], [ 'elem2', 2 ], [ 'elem3', 3 ] ]
You can do it like this:
const array1 = [0, 20, 40, 60, 80];
const array2 = ["-97:61:-1008;", "-97:60:-1008;", "-97:73:-1006", "-98:70:-1008"];
const arrayFinal = array1.map((item,index) => `${item}:${array2[index]}`);
If you are sure both arrays have the same length you can loop through the first one with a map and do something like that:
const arrayFinal = array1.map((item, i) => ({[item]: array2[i]}));
You'll get:
arrayFinal = [{elem1: elemA}, {elem2: elemB}...]
const array1 = ['item1', 'item2', 'item3'];
const array2 = [1, 2, 3];
const arrayFinal = array1.map((item, i) => ({[item]: array2[i]}));
console.log(arrayFinal);
Your case is very close to the following utility functions:
https://lodash.com/docs/4.17.15#zip
https://ramdajs.com/docs/#zip
Also, it could be implemented as follows:
function zip<A, B>(listA: A[], listB: B[]): Array<[A, B]> {
return listA.map((a, i) => [a, listB[i]]);
}
https://www.typescriptlang.org/play?#code/GYVwdgxgLglg9mABALxgBwDwEEA0iBCAfABQA2MAzlFgFyJYDaAunuVfnfswJR1YBO-AIYBPDA1wEmhRAG8AUIkT8AplBD8kbagDoAtkLTFiQvDG6IAvDIanE2rjCZNuAbnkBfefIgIKcUhUdUjgAc2JUIwYARhwAJhwAZhZEBgAWHABWHAA2FzcgA
If you want a dictionary:
function zipObject<A extends (string | number | symbol), B>(listA: A[], listB: B[]): { [key in A]: B } {
return Object.assign(
{},
...listA.map((a, i) => ({ [a]: listB[i] })),
);
}

Can I use map function with 2 arrays?

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)

Repeat an array with multiple elements multiple times in JavaScript

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));

Convert ES6 Iterable to Array

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);
}

Categories