Convert a list into an array - javascript

I have a function that takes as an argument a list and must return the elements of the list into an array.
For example in case of the input:
{value: 1, rest: {value: 2, rest: null}}
The output should looke like:
[1, 2]
This is how I tried to solve it:
function listToArray(list){
var arr = [];
for (var node = list; node; node = node.rest) {
arr.unshift(node);
}
return arr;
}
console.log(listToArray({value: 1, rest: {value: 2, rest: null}}));
And the output I get is:
[{value: 2, rest: null}, {
value: 1
rest: {value: 2, rest: null}
}]
Does anyone know what should I change to make it work?

You were just missing the .value from node.
function listToArray(list){
var arr = [];
for (var node = list; node; node = node.rest) {
arr.unshift(node.value);
}
return arr;
}
console.log(listToArray({value: 1, rest: {value: 2, rest: null}}));
Note you might want push instead of unshift.

You can use recursion to grab all the inner object values.
var obj = {value: 1, rest: {value: 2, rest: null}};
var list = objToList(obj, 'value', 'rest');
console.log(list);
function objToList(obj, valueField, targetField) {
return objToListInner(obj, valueField, targetField, []);
}
function objToListInner(obj, valueField, targetField, list) {
if (isObject(obj)) {
list.push(obj[valueField]);
objToListInner(obj[targetField], valueField, targetField, list)
}
return list;
}
function isObject(obj) {
return obj !== null && typeof obj === 'object';
}
.as-console-wrapper {
top: 0;
max-height: 100% !important;
}
A bit of code golf. ;)
let obj = {value: 1, rest: {value: 2, rest: null}},
list = objToList(obj, 'value', 'rest');
console.log(list);
function objToList(o, v, t, l) {
return o ? objToList(o[t], v, t, (l||[]).concat(o[v])) : (l||[])
}
.as-console-wrapper {
top: 0;
max-height: 100% !important;
}

Related

Format data for chart

I'm having some trouble formatting/transforming some simple data into a format that I can use to graph, and I'm hoping someone might help me solve. Currently, I have something like this
somedata=
{test1: {good: 3, bad: 2, redo: 2}}
{test2: {good: 4, bad: 3}}
{test3: {good: 3, redo: 4}}
into something like
series:
[{name: "good", data: [3,4,3]},
{name: "bad", data: [2,3,0]},
{name: "redo", data: [2,0,4]}]
I can grab the categories by using Object.keys(somedata) easy enough i.e. ['test1', 'test2', 'test3'] but having problem formatting the rest of the data. I tried something like
let combine = {};
Object.values(somedata).map((row) => {
for (const [key, value] of Object.entries(row)) {
combine.hasOwnProperty(key)
? combine[key].push(value)
: (combine[key] = [value]);
}
console.log("combined", combine);
});
but quickly realized that it won't add 0 when key doesn't exist, which is required for the chart to compare between the different series, such as bar charts. So, any help is appreciated.
You can first collect all unique values and then using array#reduce and other array methods generate all the values corresponding to each key in an object accumaltor.
const somedata = [{test1: {good: 3, bad: 2, redo: 2}}, {test2: {good: 4, bad: 3}}, {test3: {good: 3, redo: 4}}],
uniqueValues = [...new Set(
somedata.reduce((r,o) => {
Object.values(o).forEach(ob => {
r.push(...Object.keys(ob));
});
return r;
}, [])
)];
result = Object.values(somedata.reduce((r, o) => {
Object.values(o).forEach(ob => {
uniqueValues.forEach(k => {
r[k] = r[k] || { name: k, data: []};
ob[k] ? r[k].data.push(ob[k]): r[k].data.push(0);
});
});
return r;
},{}));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
something like this: for each test, group categories (can optionally restrict to a subset) - assume Zero for missing category
const someData = {test1: {good: 3, bad: 2, redo: 2}, test2: {good: 4, bad: 3}, test3: {good: 3, redo: 4}};
function prepMyGraphData(data, fields) {
let out = {
}
for (const [k, el] of Object.entries(data)) {
const _fields = new Set((fields || Object.keys(el)).concat(Object.keys(out)));
for (const f of _fields) {
const v = el.hasOwnProperty(f) ? el[f] || 0 : 0 ; // own field or 0
if (out.hasOwnProperty(f)) {
out[f].data.push(v) // existing category
}else{
out[f] = {name: f, data: [v]} // new category entry
}
}
}
return Object.values(out)
}
let fields = ['good', 'bad', 'redo']; // OR, undefined, for ALL own properties
const data = prepMyGraphData(someData, fields);

Convert list object into array

I have a list object
{
value: 5,
rest: {
value: 10,
rest: {
value: 15,
rest: null
}
}
}
that should be converted into array. I was trying to iterate through the list to take the values and push them into array.
function listToArr(obj){
let arr = []
for (let val in object){
arr.push(Object.values(val))
}
return arr
}
But I am getting [ [ 'v', 'a', 'l', 'u', 'e' ], [ 'r', 'e', 's', 't' ] ]
You'd need to reassign the object inside a loop and access its value property:
console.log(listToArr({ value: 5, rest: { value: 10, rest: { value: 15, rest: null } } }));
function listToArr(obj){
const arr = [];
while (obj.rest) {
arr.push(obj.value);
obj = obj.rest;
}
arr.push(obj.value);
return arr;
}
Since the keys are static, using Object.values or for..in doesn't accomplish anything.
An ES6 style approach:
let listToArr = (obj) => obj.rest ? [obj.value, ...listToArr(obj.rest)] : [obj.value];
console.log(listToArr({ value: 5, rest: { value: 10, rest: { value: 15, rest: null } } }));
There's a nice recursive solution for this as well. Something like:
let a = {
value: 5,
rest: {
value: 10,
rest: {
value: 15,
rest: null
}
}
}
function listToArr(obj, arr){
arr = arr || [];
if (!obj) return arr;
return listToArr(obj.rest, arr.concat(obj.value));
}
console.log(listToArr(a));

How to create Object with nested Objects from an Array

I have an array [1, 2, 3] and I want to transfer it to object with nested parent-child objects's series like this :
{ value: 1, rest: { value: 2, rest: { value: 3, rest: null } }
If I have an array [1, 2, 3, 4] the result will be like this :
{ value: 1, rest: { value: 2, rest: { value: 3, rest: { value:4, rest:null } }
The best effort of me is this snippet of code :
const arrayToList = (array) => {
let list = { value: null, rest: null };
for (let e of array) {
array.indexOf(e) === 0 && (list.value = e);
array.indexOf(e) >= 1 && (list.rest = { value: e });
}
return list;
};
console.log(arrayToList([1, 2, 3]));
You can use reduceRight like so:
let obj = arr.reduceRight((rest, value) => ({ value, rest }), null);
It starts building the object from the inside out; it starts by creating the innermost object and then it uses that object as the rest property for the next outer object and so on until there are no more items in the array.
Demo:
let obj = [1, 2, 3, 4].reduceRight((rest, value) => ({ value, rest }), null);
console.log(obj);
You can create such object by running below recursive function:
let arr = [1, 2, 3, 4];
let transform = (arr, obj) => {
if(arr.length === 0){
return obj;
} else {
let last = arr[arr.length - 1];
let newArr = arr.slice(0, arr.length - 1);
return transform(newArr, { value: last, rest: obj || null })
}
};
console.log(transform(arr));
Use a recursive function:
let array = [1, 2, 3];
function arrayToL(array) {
let el = array.splice(0, 1)[0];
let rtn = {
value: el
}
rtn.rest = (array.length > 0) ? arrayToL(array) : null;
return rtn;
}
console.log(arrayToL(array));
I suggest another solution using the spread operator and reversing the array and start building object from the array end :
let arr = [1, 2, 4, 5]
let obj = {} //object to be built
arr.slice().reverse().forEach(item => { //i used the slice method
//in order to avoid mutating
//the original variable
obj = { ...obj,
...{
value: item,
rest: obj
}
};
})
console.log(obj)

Flattening nested array in JavaScript

I have a horrible looking array which looks like this:
EDIT:
array = [
{
Letters: [{ Letter: 'A' }, { Letter: 'B' }, { Letter: 'C' }],
Numbers: [{ Number: '1' }, { Number: '2' }, { Number: '3' }]
},
null,
{
Letters: [{ Letter: 'D' }, { Letter: 'E' }, { Letter: 'F' }, { Letter: 'G' }, { Letter: 'H' }],
Numbers: [{ Number: '4' }, { Number: '5' }, { Number: '6' }, { Number: '7' }]
}
];
And want the array to look like this:
flattenedArray = [a,b,c,1,2,3,d,e,f,g,h,4,5,6,7]
Unfortunately I cannot change the original formatting because that is the form received when merging two API responses that I am getting.
I have tried using:
var flattenedArray = [].concat.apply([], array);
But it just presents the array in the same format it was entered in.
I was wondering if anybody had any advice?
EDIT:
I have tried implementing the suggestions given - thank you so much for your help. It seems it is a problem with the format of the list - unfortunately using the chrome console which is in a 'tree' format I cannot see the direct structure of the array output.
Thank you for all your help!
EDIT 2: See above for the actual array, thank you for showing me how to see this!
If you have lodash, you can use:
_.flattenDeep(array)
You can also checkout their source code for ides on how to implement yourself if you prefer.
Edit for the new request of nested arrays/objects and the flattening, you could use a combined approach with testing for the type of an element.
var array = [{ Letters: [{ Letter: 'A' }, { Letter: 'B' }, { Letter: 'C' }], Numbers: [{ Number: '1' }, { Number: '2' }, { Number: '3' }] }, null, { Letters: [{ Letter: 'D' }, { Letter: 'E' }, { Letter: 'F' }, { Letter: 'G' }, { Letter: 'H' }], Numbers: [{ Number: '4' }, { Number: '5' }, { Number: '6' }, { Number: '7' }] }],
result = array.reduce(function iter(r, a) {
if (a === null) {
return r;
}
if (Array.isArray(a)) {
return a.reduce(iter, r);
}
if (typeof a === 'object') {
return Object.keys(a).map(k => a[k]).reduce(iter, r);
}
return r.concat(a);
}, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Old request and the immortal question how to flat a nested array.
var flat = (r, a) => Array.isArray(a) ? a.reduce(flat, r) : r.concat(a),
inputArray = array = [[['a', 'b', 'c'], [1, 2, 3]], [], [['d', 'e', 'f', 'g', 'h'], [4, 5, 6, 7]]],
outputArray = inputArray.reduce(flat, []);
console.log(outputArray);
You can create recursive function using forEach() that will return new array.
var array = [[['a','b','c'],[1,2,3]],[],[['d','e','f','g','h'],[4,5,6,7]]]
function flat(data) {
var r = []
data.forEach(e => Array.isArray(e) ? r = r.concat(flat(e)) : r.push(e));
return r;
}
console.log(flat(array))
You can also use reduce() instead of forEach()
var array = [[['a','b','c'],[1,2,3]],[],[['d','e','f','g','h'],[4,5,6,7]]]
function flat(data) {
return data.reduce((r, e) => Array.isArray(e) ? r = r.concat(flat(e)) : r.push(e) && r, [])
}
console.log(flat(array))
As #Bergi suggested you can use reduce() like this.
data.reduce((r, e) => r.concat(Array.isArray(e) ? flat(e) : [e]), [])
It's nice to use a recursive function for such cases:
arr = [[['a','b','c'],[1,2,3]],[],[['d','e','f','g','h'],[4,5,6,7]]];
function flatten(arr) {
var result = [];
for (var i = 0, len = arr.length; i < len; i++) {
result = result.concat(Array.isArray(arr[i])? flatten(arr[i]) : [arr[i]]);
}
return result;
}
console.log(flatten(arr));
You could try the flatten function in Ramda.
R.flatten([1, 2, [3, 4], 5, [6, [7, 8, [9, [10, 11], 12]]]]);
//=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
Your Array format is not correct, you are missing commas(,). This is correct array.
var array = [[['a','b','c'],[1,2,3]],[],[['d','e','f','g','h'],[4,5,6,7]]];
var array = [[['a','b','c'],[1,2,3]],[],[['d','e','f','g','h'],[4,5,6,7]]];
var result = flatten(array);
function flatten(array) {
var flat = [];
if(array !== undefined){
var flat = [];
for (var i = 0; i < arguments.length; i++) {
if (arguments[i] instanceof Array) {
flat = flat.concat(flatten.apply(null, arguments[i]));
} else {
flat.push(arguments[i]);
}
}
}
return flat;
}
console.log(result);
No one thought of splicing in-place?
function flatten(array){
for (var i = 0; i < array.length; i++) {
if(array[i] instanceof Array){
array.splice.apply(array,[i,1].concat(array[i]));
i--;
}
};
return array;
}
One iteration, no recursion.
Implement flatten function using recursion and spread operator.
const a = [1,[2,[3,4],[5]],6];
const flatten = (arr) => {
const res = []
for(let i=0;i<arr.length;i++) {
if(!Array.isArray(arr[i])) res.push(arr[i]);
else res.push(...flatten(arr[i]));
}
return res;
}
console.log(flatten(a));
function steamrollArray(arr) {
var tmp = [];
arr.forEach(function(val){
if(Array.isArray(val))
tmp = tmp.concat(steamrollArray(val));
else
tmp.push(val);
});
console.log(tmp);
return tmp;
}
steamrollArray([1, [2], [3, [[4]]]]);
let arr = [1,2,[3,4]]
/* let newarr = arr.flat(); */
let newarr = Object.values(arr);
let arr2 = []
for(let val of Object.values(arr)) {
if(!Array.isArray(val)){
console.log(val)
arr2.push(val)
}
for ( let val2 of Object.values(val)){
arr2.push(val2)
}
}
console.log(arr2)

Javascript - Function on Array confusion

function arrayToList(array) {
var list = null
for (var i = 0; i < array.length; i++)
list = {value: array[i], rest: list};
return list;
}
console.log(arrayToList([10, 20, 30]));
// {value: 30, rest:{ value:20, rest:{value: 10, rest: null}}
I've looked at this function from eloquent javascript exercise for hours, and can't get it why the result are reversed. Any help would be appreciated.
Here's what's happening when you are iterating in your for-loop:
First iteration:
i = 0;
list = {value:10, rest:null}
Second iteration:
i = 1;
list = {value:20, rest:{value:10, rest: null}}
Third iteration:
i = 2;
list = {value:30, rest:{value:20, rest:{value:10, rest: null}}}
On each iteration, you are nesting your list object within itself:
list = {value: array[i], rest: list};
Set an output in the loop, than you can see how it works:
function arrayToList(array) {
var list = null
for(var i = 0; i < array.length; i++) {
list = {value: array[i], rest: list};
console.log(list);
console.log('\n');
}
return list;
}
arrayToList([10, 20, 30]);
/* Output
{ value: 10, rest: null }
{ value: 20, rest: { value: 10, rest: null } }
{ value: 30, rest: { value: 20, rest: { value: 10, rest: null } } }
*/
You have set list = null.
The first time in the loop, list is "{ value: 10, rest: null }"
-> "null" nested within.
The second time in the loop, list is "{ value: 20, rest: { value: 10, rest: null } }"
-> "{ value: 10, rest: null }" nested within.
For the last time, list is "{ value: 30, rest: { value: 20, rest: { value: 10, rest: null } } }"
-> "{ value: 20, rest: { value: 10, rest: null } }" nested within.

Categories

Resources