Flattening Arrays using javascript join, and json.parse - javascript

I have nested array like this,
var a=[1,2,[3,4,[5,6]]];
I am trying to convert it to flatten array.
var a=[1,2,[3,4,[5,6]]];
var b = a.join(',');
console.log(b);
var c = JSON.parse("[" + b + "]")
console.log(typeof c);
I used join and JSON.parse() methods, I am able to convert to a flat array, but when I do typeof for the array 'c', it says the type as 'object but not an array. My question is as everything is considered as an 'Object' in Javascript. Is returning the array 'c' as 'object' is correct or not?

Arrays are a type of object which is why it is printing object to the console. To test if an array is an array you need to use Array.isArray(someVar).
Note: You can use flat() to flatten the array.
let a = [1,2,[3,4,[5,6]]]
// 2 is the depth
console.log(a.flat(2))
console.log(typeof a)
// Prints true
console.log(Array.isArray(a))
// Prints false
console.log(Array.isArray({a:123}))
// Prints false
console.log(Array.isArray(123))
// Prints false
console.log(Array.isArray('Kitty'))

You can recursively flatten the array with reduce like this. If the item inside the array is an array, flatten the array with the same function.
const flatten = (arr) => {
return arr.reduce((acc,item) => {
if (Array.isArray(item)) {
return acc.concat(flatten(item))
}
return acc.concat(item)
}, [])
}
const a = [1,2,[3,4,[5,6]]];
console.log(flatten(a))

Try:
obj instanceof Array
Also something to consider -- if you're passing the Array across frame boundaries, you will need to do the following:
Object.prototype.toString.call(obj) === '[object Array]'
See related post: How to detect if a variable is an array

The best method you can use to check if c is an array is Array.isArray(), and it gives true for c so you are "safe":
var a=[1,2,[3,4,[5,6]]];
var b = a.join(',');
var c = JSON.parse("[" + b + "]");
console.log(c);
console.log("c is Array?: " + Array.isArray(c));
.as-console {background-color:black !important; color:lime;}
.as-console-wrapper {max-height:100% !important; top:0;}
And yes, it is ok the typeof for an array gives object because from MDN:
Arrays are list-like objects whose prototype has methods to perform traversal and mutation operations...
var a=[1,2,[3,4,[5,6]]];
console.log(typeof a);
var b = new Array(10);
console.log(typeof b);
.as-console {background-color:black !important; color:lime;}
.as-console-wrapper {max-height:100% !important; top:0;}
Now, if you need to flatten your array, you can use the experimental Array.flat() it takes and optional deep argument (default to 1). However note this method can not be available on some browser/version. In this, can you can use the next version with recursion:
const flattenDeep = (arr) =>
{
return arr.reduce(
(acc, val) => Array.isArray(val) ? acc.concat(flattenDeep(val)) : acc.concat(val),
[]
);
}
console.log(flattenDeep([1,2,[3,4,[5,6]]]));
.as-console {background-color:black !important; color:lime;}
.as-console-wrapper {max-height:100% !important; top:0;}

Related

add array of keys as property on obj as accumulator by reduce?

i have array of keys that i want to add as property with calculated val(not including on code) on empty object like this by reduce:
const f = ['a','b','c'].reduce((obj,key) => obj[key]='', {})
i was expecting the obj is the accumulator {} so i added properties that way? how do i make this work by reduce?
i was expecting and want it to result like this for that code:
{ a:'', b:'', c:'' }
but my code only results to empty string on console. how do i achieve it?
The value that the reduce callback returns will be the accumulator in the next iteration. So, in your code, obj only refers to the object in the first iteration - on the next iteration, it refers to what obj[key] = '' resolves to, which is the empty string.
Return the object instead:
const f = ['a', 'b', 'c'].reduce((obj, key) => {
obj[key] = '';
return obj;
}, {});
console.log(f);
You could use Object.fromEntries instead, if you wanted (though, it's very new, so for good cross-browser support, include a polyfill):
const f = Object.fromEntries(
['a', 'b', 'c'].map(key => [key, ''])
);
console.log(f);

How to list of objects into list of arrays in js?

I have an array of data [{a:12,b:20},{a:20,b:123}]
How I can convert this to [[12,20],[20,123]]
You can use Array.map() using Object.Values() as the mapping method:
let input = [{a:12,b:20}, {a:20,b:123}];
let res = input.map(Object.values);
console.log(JSON.stringify(res));
.as-console {background-color:black !important; color:lime;}
.as-console-wrapper {max-height:100% !important; top:0;}
If you need to rely on order of the keys, then refer to #MarkMeyer answer, it can be more appropriate for your purposes.
It's a pretty one-liner with some destructuring:
let l = [{a:12,b:20},{a:20,b:123}]
let arr = l.map(({a, b}) => ([a, b]))
console.log(arr)
const data = [{a:12,b:20},{a:20,b:123}]
let result = []
data.forEach(d => result.push([d.a,d.b]))
console.log(result)
Extract the keys and loop it with your input variable. i have used map function to loop and get data in array format.
var input = [{a:12,b:20},{a:20,b:123}];
var keys = Object.keys(input[0]);
var output = [];
keys.forEach(function(key){
output.push(input.map((item) => item[key]))
})
console.log(output)

Javascript - One-liner to extract values from object to array

I quite simple one:
I have a Javascript object with some properties whose values are arrays, with the following structure:
let obj = {emails: ["xxx#yyy.com", "qqq#www.com"], nickname: ["asdf"],...}
I need to get an array of arrays with only the values, like the following:
let obj2 = [["xxx#yyy.com"], ["qqq#www.com"], ["asdf"],...]
With Object.values(obj), I get [["xxx#yyy.com", "qqq#www.com"], ["asdf"],...], which is not exactly what I am looking for, but it is a good starting point...
Also, I am looking for a one-liner to do it, if possible. Any ideas?
Thanks.
An alternative using the function reduce.
This approach adds objects and arrays from the first level.
As you can see, this approach evaluates the type of the object.
let obj = {emails: ["xxx#yyy.com", "qqq#www.com"], nickname: ["asdf"]}
var result = Object.values(obj).reduce((a, c) => {
if (Array.isArray(c)) return a.concat(Array.from(c, (r) => [r]));
return a.concat([c]);
}, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
One line approach (excluding the checking for array type):
let obj = {emails: ["xxx#yyy.com", "qqq#www.com"], nickname: ["asdf"]},
result = Object.values(obj).reduce((a, c) => (a.concat(Array.from(c, (r) => [r]))), []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can use Object.values to get array of values and then concat and spread syntax ... to get flat array and then map method.
let obj = {emails: ["xxx#yyy.com", "qqq#www.com"], nickname: ["asdf"]}
const values = [].concat(...Object.values(obj)).map(e => [e])
console.log(values)

How to transform object into sorted array by Lodash

How to transform {2:'b',3:'c',1:'a'} into [{1:'a'},{2:'b'},{3:'c'}] by lodash?
It's fairly trivial using Object.keys + Array.map, you really don't need lodash:
const obj = {2:'b',3:'c',1:'a'};
const arr = Object.keys(obj).map(key => ({ [key]: obj[key] }))
console.log(arr)
Regarding the lack of a sort function, the above code is exploiting the fact that numerically indexed Object keys are (per the spec) stored sequentially. Check the order for yourself:
console.log({2:'b',3:'c',1:'a'})
Here is the relevant portion of the spec
9.1.12 [[OwnPropertyKeys]] ( )
When the [[OwnPropertyKeys]] internal method of O is called the
following steps are taken:
Let keys be a new empty List.
For each own
property key P of O that is an integer index, in ascending numeric
index order
2a. Add P as the last element of keys.
With upcoming Javascript with Object.entries, you could map a new array with single objects.
var data = {2:'b',3:'c',1:'a'},
result = Object
.entries(data)
.sort((a, b) => a[0] - b[0])
.map(([k, v]) => ({ [k]: v }));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
With lodash, you could use
_.chain,
_.toPairs,
_.sortBy,
_.map and
_.fromPairs
var data = {2:'b',3:'c',1:'a'},
result = _
.chain(data)
.toPairs(data)
.sortBy([0])
.map(o => _.fromPairs([o]));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.15.0/lodash.min.js"></script>
Lodash is not really necessary to accomplish what you want, but I'm still adding it anyway and add a sorted function. I've also included the native JavaScript way.
const obj = {b: 3, c: 2, a: 1};
const sortByKeys = object => {
const keys = Object.keys(object)
const sortedKeys = _.sortBy(keys)
return _.map(sortedKeys, key => ({ [key]: object[key]}))
}
// the lodash way, and sorted
console.log(sortByKeys(obj))
// simpler way
const result = Object.keys(obj)
.map(key => ({ [key]: obj[key] }))
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>
Why use lodash? Just use regular Javascript. Solution can be cleaned up a bit but the idea is to loop through your object and push your desired format into a new array. I also throw the sorting in there for convenience, but feel free to re-factor to your liking.
const obj = {2:'b',3:'c',1:'a'}
let newArr = [];
for (var key in obj) {
newArr.push({[key]: obj[key]})
newArr.sort((a, b) => a[key] > b[key])
}
console.log(newArr)

Any easy way to merge, flattern and concat multiple multi-dimensional arrays in javascript in a certain way (right to left)?

I need an easy way to merge, flattern and concat multiple multi-dimensional arrays in javascript in a certain way (right to left)
# Exemple
[['.class1', '.class2'], ['.class3', ['.class4', '.class5', ...], ['.class6'], ...]]
# OR
[['.class1', '.class2'], ['.class3', ['.class4', '.class5', ...]], ['.class6'], ...]
# Would become
['.class1.class3.class4.class6', '.class1.class3.class5.class6', '.class2.class3.class4.class6', '.class2.class3.class5.class6', ...]
I've found the reduceRight function from underscore.js lib but I'm not sure how i could acheive this easily as it needs to be done recursively.
You could use an iterative and recursive approach for variable length of parts with Array#forEach.
This version works now with nested arrays and flats it in advanced.
function combine(array) {
function c(part, index) {
var temp = array[index];
if (Array.isArray(temp) && temp.some(function (a) { return Array.isArray(a); })) {
temp = combine(array[index].map(function (a) { return Array.isArray(a) ? a : [a]; }));
}
temp.forEach(function (a) {
var p = part.concat(a);
if (p.length === array.length) {
r.push(p.join(''));
return;
}
c(p, index + 1);
});
}
var r = [];
c([], 0);
return r;
}
var array = [
['.class1', '.class2'],
['.class3',
['.class4', '.class5'],
['.class6']
]
],
result = combine(array);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
var data = [['.class1', '.class2'], ['.class3', ['.class4', '.class5'], ['.class6']]];
function cartesian(a, b) { // return the cartesian product of the two arrays a and b
if(!a.length) return b; // if a is empty then the result of the product is b
if(!b.length) return a; // if b is empty then the product is a
return a.reduce((res, ae) => (b.forEach(be => res.push(ae + be)), res), []); // product of non-empty a and non-empty b
}
function combos(arr) { // take an array arr and return the combinations out from its nested arrays
if(arr.every(e => !Array.isArray(e))) return arr; // if the array doesn't contain any nested arrays then return it as it is
return arr.reduce((acc, e) => { // otherwise return the cartesian product of all its elements
e = Array.isArray(e)? combos(e): [e]; // if the current element is an array, then get its combos, otherwise, wrap it in an array
return cartesian(acc, e); // get the cartesian product of previous elements and the combos of this element e
}, []);
}
console.log(combos(data));
If you're open to using lodash, which is a better version of underscore (imo), this can be expressed fairly simply using flattenDeep (https://lodash.com/docs/4.17.4#flattenDeep) on each element of your multi-dimensional array:
function combine(list) {
const arrays = list.map(_.flattenDeep);
return arrays.map((a) => a.join('');
}

Categories