map non-object values in nested arrays in javascript - javascript

I'm trying to map an array that has n-dimension sub-arrays. I've looked at deep-map but I'm not using any objects. An example of what I'm trying to do is:
deepMap([4,5,[3,4,[2]]], (x) => x+5)
returns [9,10,[8,9,[7]]]
The function after the array in deepMap can be any function
const deepMap=(arr, fn) =>{
const stop = arr.length===0;
const tail = arr.slice(1);
const head = arr[0];
const front = head instanceof Array ? [head[0]]: [] ;
const next = fn(front, head);
return stop ? [] : front.concat(deepMap(tail,fn));
}
How do you apply a function to values in nested arrays while keeping the whole array a nested array?
any help is greatly appreciated!

You may do as follows in a Haskellesque fashion;
function deepMap([x,...xs],f){
return x ? [Array.isArray(x) ? deepMap(x,f) : f(x), ...deepMap(xs,f)]
: [];
}
var arr = [4,5,[3,4,[2]]],
res = deepMap(arr, x => x+5);
console.log(res);

Use simple recursion. For a nested array, map over the array calling deepMap recursively. When you reach a leaf, call the function.
function deepMap(arr, fn) {
return arr instanceof Array ? arr.map(el => deepMap(el, fn)) : fn(arr);
}
console.log(deepMap([4, 5, [3, 4, [2]]], (x) => x + 5));

Related

lodash flatten inner array (part of object) and generate as many object based on array length

I have the following Array of Objects. I want to generate as many Objects based on inner 'Y' array length.
var arr = [{x:1,y:[1,2]},{x:2,y:[1,2]}];
Expected Output as follows
var arr = [{x:1,y:1},{x:1,y:2},{x:2,y:1},{x:2,y:2}]
Code I have tried but i could't
arr.forEach(item => {
return item.y.forEach(inner => {
return inner;
})
})
You can use flatMap() with nested map(). Use flatMap() on the main array and inside that use map() on y property of that object. return an object from inner map() function whose y property will be different and x will be the same
var arr = [{x:1,y:[1,2]},{x:2,y:[1,2]}];
const res = arr.flatMap(({x, y}) => y.map(y => ({x, y})));
console.log(res)
If you don't understand the flatMap below is the version using nested forEach
var arr = [{x:1,y:[1,2]},{x:2,y:[1,2]}];
const res = [];
arr.forEach(a => {
a.y.forEach(y => {
res.push({x: a.x, y});
})
})
console.log(res)

Find index of any array using value in string array

a=[
{x:1,y:1,i:"Piechart1"},
{x:2,y:1,i:"Piechart2"},
{x:3,y:1,i:"Piechart3"}
]
str=["Piechart1","Piechart3"];
I want get index by comparing array string.Output in above example should be [0,2]
Could you please let me know how to achieve in lodash ,javascript
Use .map() to map the strings to their index, and .findIndex inside the .map() callback to locate the index of the object.
var a = [{x:1,y:1,i:"Piechart1"},{x:2,y:1,i:"Piechart2"},{x:3,y:1,i:"Piechart3"}];
var str = ["Piechart1","Piechart3"];
var res = str.map(s => a.findIndex(o => o.i == s));
console.log(res);
You can chain .filter(idx => idx != -1) on the end if there's any chance of one of the strings not being in the main array.
You can use reduce() method and includes() to check if element exists in another array.
const a = [{"x":1,"y":1,"i":"Piechart1"},{"x":2,"y":1,"i":"Piechart2"},{"x":3,"y":1,"i":"Piechart3"}]
const str = ["Piechart1", "Piechart3"];
const result = a.reduce((r, {i}, ind) => {
return str.includes(i) && r.push(ind), r
}, [])
console.log(result)
maps each value in str to their index in a.
str.map((str) => a.findIndex((ele) => str === ele.i))

Compare arrays of objects independent of the order

I have 2 arrays of objects and I have to compare them, but the order of the objects DOES NOT matter. I can't sort them because I won't have their keys' names because the functions must be generic. The only information that I'll have about the array is that both array's objects have the same amount of keys and those keys have the same name. So the array1 must contain the same objects as the array2.
var array1 = [{"key1":"Banana", "key2":"Yammy"}, {"key1":"Broccoli", "key2":"Ew"}];
var array2 = [{"key1":"Broccoli", "key2":"Ew"}, {"key1":"Banana", "key2":"Yammy"}];
In the example, array1 must be equal array2.
I tryed to use the chai .eql() method but it didn't work.
The following solution:
will verify that the arrays have an equal number of elements
does not impose restrictions on keys (as to not contain a certain delimiter)
requires both keys and (string) values to be the same
has a time complexity of O(nlogn) (instead of O(n²) as some other solutions here)
function equalArrays(a, b) {
if (a.length !== b.length) return false;
const ser = o => JSON.stringify(Object.keys(o).sort().map( k => [k, o[k]] ));
a = new Set(a.map(ser));
return b.every( o => a.has(ser(o)) );
}
// Example
var array1 = [{"key1":"Banana", "key2":"Yammy"}, {"key1":"Broccoli", "key2":"Ew"}];
var array2 = [{"key1":"Broccoli", "key2":"Ew"}, {"key1":"Banana", "key2":"Yammy"}];
console.log(equalArrays(array1, array2)); // true
// Example with different key name
var array1 = [{"key0":"Banana", "key2":"Yammy"}, {"key1":"Broccoli", "key2":"Ew"}];
var array2 = [{"key1":"Broccoli", "key2":"Ew"}, {"key1":"Banana", "key2":"Yammy"}];
console.log(equalArrays(array1, array2)); // false
You can array#join each value of the object on an separator and then generate a new array of string and then compare each values using array#every and array#includes
var array1 = [{"key1":"Banana", "key2":"Yammy"}, {"key1":"Broccoli", "key2":"Ew"}];
array2 = [{"key1":"Broccoli", "key2":"Ew"}, {"key1":"Banana", "key2":"Yammy"}];
values = (o) => Object.keys(o).sort().map(k => o[k]).join('|'),
mapped1 = array1.map(o => values(o)),
mapped2 = array2.map(o => values(o));
var res = mapped1.every(v => mapped2.includes(v));
console.log(res);
You can do something like following:
For each object in each array you can calc its representation:
arr1.forEach( (obj) => {
obj.representation = '';
for (let key of Object.keys(obj)) {
obj.representation += obj[key];
}
}
Same for arr2
now you can sort both arrays by representation for example and then compare.
To sort do the following:
arr1.sort( (a,b) => { return a.representation > b.representation } );
arr2.sort( (a,b) => { return a.representation > b.representation } );
After sorting you can compare both arrays
let equal = arr1.every( (el, i) => arr2[i]===el );

use lodash javascript to full join two-dimensional array

I have two two-dimensional array.
var arr1=[[1,20],[2,30]];
var arr2=[[2,40],[3,50]];
This is expected output:
[[1,20,null],[2,30,40],[3,null,50]]
It is like full join of two data frames. The logic is similar to this pseudo code:
df1.join(df2, df1[col_1] == df2[col_1], 'full')
but this case is for two-dimensional array. Can lodash do this? If not, how to do it in vanilla javascript?
Well, lodash can't do this, but we can:
function flatten2d(arr1, arr2) {
const o1 = _.fromPairs(arr1);
const o2 = _.fromPairs(arr2);
const result = [];
_.forEach(o1, (v, k) => {
const v2 = o2[k] || null;
result.push([k|0, v, v2]);
delete o2[k];
});
// at this point, only items non-existing
// in o1 are left in o2
_.forEach(o2, (v, k) => {
result.push([k|0, null, v]);
});
return result;
}
Testing:
flatten2d([[1,20],[2,30]], [[2,40],[3,50]])
Result:
[[1,20,null], [2,30,40], [3,null,50]]
If you don't have duplicate id's in any single array then you can try combine them, and group them using groupBy with first element (i.e. the key 0), and if you have duplicates, anyway I am not sure what exactly output you are looking for! Here is what you can do:
_(arr1.concat(arr2)).groupBy('0').map(v=>
[v[0][0]].concat(v.length > 1 ? v.map(m=>m[1]) : [v[0][1], null])
).value();
Here is an working snippet for you:
var arr1=[[1,20],[2,30]];
var arr2=[[2,40],[3,50]];
var res = _(arr1.concat(arr2)).groupBy('0').map(v=>
[v[0][0]].concat(v.length > 1 ? v.map(m=>m[1]) : [v[0][1],null])
).value();
console.log(JSON.stringify(res));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>

Recursion issue using Javascript Issue

I am working on this challenge from FreeCodeCamp
Flatten a nested array. You must account for varying levels of
nesting.
I am trying to solve it using recursion.
Here is my code:
function steamroller(arr) {
var flatArray = [];
function flatten(obj) {
if (!Array.isArray(obj)) {
return(obj);
}
for (var i in obj) {
return flatten(obj[i]);
}
}
flatArray.push(flatten(arr));
console.log(flatArray);
}
steamroller([1, [2], [3, [[4]]]]);
This logs:
Array [ 1 ]
I can see the problem, the return statement breaks the for loop so only the first value is returned.
However if I leave out the return and just use:
flatten(obj[i]);
I get back:
Array [ undefined ]
What should I do to fix this?
You need to add the items to the result array.
function flatten(arr){
var flat = [];
arr.forEach(function(item){
flat.push.apply(flat, Array.isArray(item) ? flatten(item) : [item]);
});
return flat;
}
I modified your code so that it works, adding comments:
function steamroller(arr) {
var flatArray = [];
function flatten(obj) {
if (!Array.isArray(obj)) {
// We got to the innermost element. Push it to the array.
flatArray.push(obj);
return;
}
for (i = 0; i < obj.length; i++) {
flatten(obj[i]); // Do not return here.
}
}
flatten(arr);
console.log(flatArray);
}
steamroller([1, [2], [3, [[4]]]]);
Here is a slightly different approach:
function flat(a,f){
if (!f) var f=[];
a.forEach(function(e){
if (Array.isArray(e)) flat(e,f);
else f.push(e);
});
return f;
}
var flatArray=flat(deepArray);
The function returns the flattened array each time but ignores its return value in the inner calls of itself (flat(e,f)). Instead it keeps pushing each non-Array value to the same f-Array that was defined initially in the outer call of flat(a).
Using the second (usually unused) argument you can also concatenate values to an already existing flat array like
var a=[[2,4,[5,6,[7,8],[9,10]],1],3];
var fl1=flat(a);
// "2|4|5|6|7|8|9|10|1|3"
var fl2=flat(a,fl1);
// "2|4|5|6|7|8|9|10|1|3|2|4|5|6|7|8|9|10|1|3"
A fully recursive solution, if you don't need to keep the original array intact:
function flatten (obj, memo) {
memo = (memo || []);
if (Array.isArray(obj)) {
if (obj.length) {
// flatten the first element, removing it from the original array
flatten(obj.shift(), memo);
// flatten the rest of the original array
flatten(obj, memo);
}
return memo;
}
return memo.push(obj);
}
We firstly check if obj is an array. Then, if it is not empty, flatten the first element, them flatten the rest. If object is not an array, it is returned and pushed into the memo array, wich is returned.
Notice that in the first line I'm setting memo = (memo || []). This makes sure we always get a new array in the first call, if memo is not set.
function steamrollArray(arr) {
let string = String(arr);
let strArr = string.split(",");
for (let i in strArr){
if (strArr[i]==""){
strArr.splice(i,1);
}
if (strArr[i]=="[object Object]"){
strArr[i] = {};
}
if (isNaN(parseInt(strArr[i],10))== false){
strArr[i] = parseInt(strArr[i]);
}
}
return strArr;
}
Because this was recently brought back up with a new answer, perhaps it's time to revisit it.
This is a fairly simple recursive process. When this question was asked, ES5 was ubiquitous and ES6/ES2015 was being implemented everywhere.
A simple ES5 solution would look like this:
const flat = (ns) =>
ns .map (n => Array .isArray (n) ? flat (n) : n)
.reduce ((a, b) => a .concat (b), [])
but this pattern was made unnecessary by ES6, and that map-reduce(concat) pattern could now be written with Array.prototype.flatMap:
const flatter = (ns) =>
ns .flatMap(n => Array .isArray (n) ? flatter (n) : n)
However, you don't even need to do this, because ES6 also introduced Array.prototype.flat, which allows us to write this:
const flattest = (ns) =>
ns .flat (Infinity)
const flat = (ns) =>
ns .map (n => Array .isArray (n) ? flat (n) : n)
.reduce ((a, b) => a .concat (b), [])
const flatter = (ns) =>
ns .flatMap(n => Array .isArray (n) ? flatter (n) : n)
const flattest = (ns) =>
ns .flat (Infinity)
const data = [1, [2], [3, [[4]]]]
console .log (flat (data))
console .log (flatter (data))
console .log (flattest (data))

Categories