array data type manipulation - javascript

I have an array of arrays of objects.
so it looks something like
[[{}{}],[{}{}{}],[{}{}], [{}{}{}{}]]...etc
I need to loop through each object in this array. Problem is that would require a nested for loop, which isn't bad but I was wondering if there is any way I could use the spread operator when I'm putting it in the original array.
outerArray.push(...innerArray), something along the lines of that. That didn't work but is there something similar?

You can use Array.prototype.flat to convert a nested array, into a flattened array
var arr1 = [1, 2, [3, 4]];
arr1.flat();
// [1, 2, 3, 4]
var arr2 = [1, 2, [3, 4, [5, 6]]];
arr2.flat();
// [1, 2, 3, 4, [5, 6]]
var arr3 = [1, 2, [3, 4, [5, 6]]];
arr3.flat(2);
// [1, 2, 3, 4, 5, 6]
For older browsers, you can refer to other answers

Just adding another option here that doesn't require passing the depth in:
const deepFlatten = arr =>
[].concat(...arr.map(v => (Array.isArray(v) ? deepFlatten(v) : v)))
call with deepFlatten(outerArray)

Related

shallow copying an array of an array

I have an array with child arrays. I'd want to shift the first element in the first child array
(arr[0].shift). But my code below shifts all first elements for all child arrays.
I figure it has something to do with fillWith being a deep-copy, but I've tried in every way I know to make it a shallow copy instead..
What am I missing?
let arr= Array(3)
let fillWith = Array.from({length: 5}, (_, i) => i+1);
arr.fill([...[], ...fillWith]) // Try 1
//arr.fill(fillWith.slice()) // Try 2
console.log(arr) // -> [[1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5]]
arr[0].shift();
console.log(arr)
/*
How I want it to be -> [[2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5]]
What it actually becomes -> [[2, 3, 4, 5], [2, 3, 4, 5], [2, 3, 4, 5]]
*/
You might do it like this:
const fillWith = () => Array.from({length: 5}, (_, i) => i + 1);
const arr = Array.from({length: 3}, fillWith)
console.log(JSON.stringify(arr))
arr[0].shift();
console.log(JSON.stringify(arr))

How to map an array of arrays and just return it

I have an array of arrays, and I want to map over it and just return the values of arrays, but when I map over it and log the result, it's just an array and I don't know how to map over my array and use it in other places.
const arr = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
];
const arrMap = arr.map((it) => it.map((itm) => itm));
console.log(arrMap);
//what I expected 1,2,3,4,5,6 , ...
//what I got [Array(3), Array(3), Array(3)]
Actually, I need the values for using them in somewhere else, but I don't know what to do.
I also used function for this but when I return the values and log them It's undefined:
const arr = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
];
const arrMap = (arr) => {
arr.forEach((element) => {
console.log(element);
//In here, everything works fine
return element;
});
};
console.log(arrMap);
//what I got undefined
Use flatMap -
const arr = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
];
const arrMap = arr.flatMap(m => m);
console.log(arrMap);
Why it won't work : map() is supposed to run on each element of an array and return a transformed array of the same length. You have three elements in your input array and will always get three elements in your mapped array.
Your expectations can be met by tweaking your code with forEach() if you want. With forEach() there is nothing returned and you will have to start with a separate array variable. Below code uses ...
const arr = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
];
let arrMap = [];
arr.forEach((it) => arrMap.push(...it));
console.log(arrMap);
But flatMap() is already there:
const arr = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
];
let ans = arr.flatMap(x => x);
console.log(ans);
Use flat if you just want to flatten the array:
const arr = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
];
console.log(arr.flat());
Use flatMap if you want to do something with each element before the array gets flattened.
const arr = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
];
const arrMap = arr.flatMap((el) => {
el.forEach((n) => console.log(n));
return el;
});
console.log(arrMap);
forEach doesn't return anything it's like a for loop but for array only.
Since you have double array you should flat it by using flatMap
const arr = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
];
const arrMap = arr.flatMap((it) => it);
console.log(arrMap);

rxjs separate emitted values in the various arrays

My service returns one array. For example:
return of([1, 2, 3, 4, 5, 6])
I subscribe to it and inside .pipe() I want to separate every 2 values to a different array.
For example:
let result = {
[1, 2],
[3, 4],
[5, 6]
}
How can I achieve this?
You can use bufferCount to create arrays from your stream:
from([1, 2, 3, 4, 5, 6]).pipe(
bufferCount(2)
).subscribe(subArray => {
console.log(subArray); // prints: [1, 2] [3, 4] [5, 6]
})

Javascript: How do I get the index of a certain order of values in an array?

I'm currently trying to create a markov chain. Right now, I need to get the index of some values in an array. Is there any way that is possible?
I tried to use indexOf, but that only supports one argument.
Let's assume there is a function named INDEX that does what I need.
const arr = [1, 2, 3, 4, 3, 2, 3, 1];
INDEX(arr, [1, 2]) // returns 0, because 1, 2 is in the first index in the array.
INDEX(arr, [3, 4, 3]) // returns 2, because 3, 4, 3 starts at the third index.
INDEX(arr, [2, 3]) // returns 1, because there are two 2, 3s, so it returns the first.
INDEX(arr, [5, 6]) // returns -1, because it is not in the array.
INDEX(arr, [1, 4]) // even though these values are in the array, they aren't in the order, so returns -1
Use .findIndex, and check whether every one of the array items follows the current index:
const arr = [1, 2, 3, 4, 3, 2, 3, 1];
const INDEX = (arr, toFind) => arr.findIndex(
(_, baseI, arr) => toFind.every((item, i) => arr[baseI + i] === item)
);
console.log(
INDEX(arr, [1, 2]), // returns 0, because 1, 2 is in the first index in the array.
INDEX(arr, [3, 4, 3]), // returns 2, because 3, 4, 3 starts at the third index.
INDEX(arr, [2, 3]), // returns 1, because there are two 2, 3s, so it returns the first.
INDEX(arr, [5, 6]), // returns -1, because it is not in the array.
INDEX(arr, [1, 4]) // even though these values are in the array, they aren't in the order, so returns -1
);
A very simple and neat approach is to deal with strings in this case.
So you can convert both the arrays to strings and use String.prototype.indexOf()
const INDEX = (arr, toFind) => arr.join('').indexOf(toFind.join(''))
const arr = [1, 2, 3, 4, 3, 2, 3, 1]
const INDEX = (arr, toFind) => arr.join('').indexOf(toFind.join(''))
console.log(
INDEX(arr, [1, 2]),
INDEX(arr, [3, 4, 3]),
INDEX(arr, [2, 3]),
INDEX(arr, [5, 6]),
INDEX(arr, [1, 4])
)

Get item what's not on the 2nd, 3rd arrays - JS

I am trying to create a function that will get the items that cannot be seen on the 2nd or 3rd and upcoming arrays passed within the function.
Right now my function gets only the similar items. How can I make it get the difference (w/c are the items that doesn't exist to the 2nd and 3rd and proceeding arrays.
const callM = function(arrays) {
arrays = Array.prototype.slice.call(arguments);
let result = [];
for(let i = 1; i < arrays.length; i++){
for(let x = 0; x < arrays[i].length; x++){
if(arrays[0].includes(arrays[i][x])){
result.push(arrays[i][x]);
}
}
}
return result;
};
console.log(callM([1, 2, 3, 4, 5], [5, 2, 10])); // -> must be [1, 3, 4]
console.log(callM([1, 2, 3, 4, 5], [5, 2, 10], [7, 1, 8])); // -> must be [3,4]
The logic right now is a bit off as it gets the opposite. How do i fix this?
Also is there a way to do this using Higher Order functions such as reduce or filter?
Thanks!
I'd think about this differently. As the difference between two sets: array 0 and array 1...n
To get array 0, just shift it off the top
const arr0 = arrays.shift()
Ref: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift
This removes the first array from arrays
Next we combine the remaining arrays
const arrN = arrays.reduce(function(prev, curr) {
return prev.concat(curr)
})
Ref: http://www.jstips.co/en/javascript/flattening-multidimensional-arrays-in-javascript/
Unneeded, handled by includes as mentioned by #Phil
Next filter duplicates from arrN by comparing with itself
const unique = arrN.filter(function(elem, index, self) {
return index == self.indexOf(elem);
})
Ref: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
Next filter with includes to find the difference (or union)
const diff = arr0.filter(function(item) {
return !arrN.includes(item))
}
Full snippet:
function callM(arrays) {
const arr0 = arrays.shift()
const arrN = arrays.reduce(function(prev, curr) {
return prev.concat(curr)
})
//const unique = arrN.filter(function(elem, index, self) {
// return index == self.indexOf(elem)
//})
return arr0.filter(function(item) {
return !arrN.includes(item)
})
}
console.log(callM([[1, 2, 3, 4, 5], [5, 2, 10]]))
console.log(callM([[1, 2, 3, 4, 5], [5, 2, 10], [7, 1, 8]]))
of course ES6 would be easier. ;)
const callM = (first, ...rest) => {
const arrays = [].concat(...rest)
return first.filter(item => !arrays.includes(item))
}
console.log(callM([1, 2, 3, 4, 5], [5, 2, 10]))
console.log(callM([1, 2, 3, 4, 5], [5, 2, 10], [7, 1, 8]))
A short solution for small and medium sized arrays:
// Return elements in array but not in filters:
function difference(array, ...filters) {
return array.filter(el => !filters.some(filter => filter.includes(el)));
}
// Example:
console.log(difference([1, 2, 3, 4, 5], [5, 2, 10])); // [1, 3, 4]
console.log(difference([1, 2, 3, 4, 5], [5, 1, 10], [7, 2, 8])); // [3, 4]
For large inputs, consider creating a Set from all filters and filtering in linear time using set.has(el).
In order to fix your implementation, you could label the outer for-loop and continue from there whenever a filter contains one of the array elements. Only when all filters pass without match, you push the array element into the result:
// Return elements in array but not in filters:
function difference(array, ...filters) {
const result = [];
loop: for (const el of array) {
for (const filter of filters) {
if (filter.includes(el)) continue loop;
}
result.push(el);
}
return result;
}
// Example:
console.log(difference([1, 2, 3, 4, 5], [5, 2, 10])); // [1, 3, 4]
console.log(difference([1, 2, 3, 4, 5], [5, 2, 10], [7, 1, 8])); // [3,4]
If you're willing to use Underscore, you can do this in one line of code:
console.log(_.difference([1, 2, 3, 4, 5], [5, 2, 10], [7, 1, 8]))
https://jsfiddle.net/o1zuaa6m/
You can use array#reduce to create object lookup of all the other array excluding the first array. Then use array#filter to get the values which are not present in the object lookup
var callM = (first, ...rest) => {
var combined = rest
.reduce((res,arr) => res.concat(arr))
.reduce((o, v) => {
o[v] = true;
return o;
},{});
return first
.filter(v => !combined[v]);
}
console.log(callM([1, 2, 3, 4, 5], [5, 2, 10])); // -> must be [1, 3, 4]
console.log(callM([1, 2, 3, 4, 5], [5, 2, 10], [7, 1, 8])); // -> must be [3,4]
The "proper" way to exclude values is usually to use a lookup hash set with the values to exclude:
const callM = (a, ...b) => (b = new Set(b.concat.apply(...b)), a.filter(v => !b.has(v)))
console.log(callM([1, 2, 3, 4, 5], [5, 2, 10])); // [1, 3, 4]
console.log(callM([1, 2, 3, 4, 5], [5, 2, 10], [7, 1, 8])); // [3, 4]

Categories