Transform object with nested values and dimensions into one-dimensional array - javascript

I have an object with following example data model
const partition = {
values: [
{
dimensionValue: 'A',
numbers: [0, 33],
partition: {
values: [
{
dimensionValue: '1',
numbers: [0, 3375],
partition: {
values: [
{
dimensionValue: 'a',
numbers: [0, 0],
partition: null
},
{
dimensionValue: 'b',
numbers: [0, 8],
partition: null
}
]
}
},
{
dimensionValue: '2',
numbers: [6028, 0],
partition: {
values: [
{
dimensionValue: 'a',
numbers: [0, 6028],
partition: null
}
]
}
}
]
}
},
{
dimensionValue: 'B',
numbers: [0, 31721.57],
partition: {
values: [
{
dimensionValue: '1',
numbers: [0, 0],
partition: {
values: [
{
dimensionValue: 'a',
numbers: [0, 31721.57],
partition: null
}
]
}
}
]
}
}
]
}
I need to transform it into one-dimensional array that will store relation between nested nodes in the dimension property, and numbers from the specific node layer in the values property. Essentially, output should look like this
[
{
"dimensions": ["A"],
"values": [0, 33]
},
{
"dimensions": ["A", "1"],
"values": [0, 3375]
},
{
"dimensions": ["A", "1", "a"],
"values": [0, 0]
},
{
"dimensions": ["A", "1", "b"],
"values": [0, 8]
},
{
"dimensions": ["A", "2"],
"values": [6028, 0]
},
{
"dimensions": ["A", "2", "a"],
"values": [0, 6028]
},
{
"dimensions": ["B"],
"values": [0, 31721.57]
},
{
"dimensions": ["B", "1"],
"values": [0, 0]
},
{
"dimensions": ["B", "1", "a"],
"values": [0, 31721.57]
}
]
My semi-working solution is here.
I'm sure there is some sexy recursive function to use here but I'm struggling to write it.
const partition = {
values: [
{
dimensionValue: 'A',
numbers: [0, 33],
partition: {
values: [
{
dimensionValue: '1',
numbers: [0, 3375],
partition: {
values: [
{
dimensionValue: 'a',
numbers: [0, 0],
partition: null
},
{
dimensionValue: 'b',
numbers: [0, 8],
partition: null
}
]
}
},
{
dimensionValue: '2',
numbers: [6028, 0],
partition: {
values: [
{
dimensionValue: 'a',
numbers: [0, 6028],
partition: null
}
]
}
}
]
}
},
{
dimensionValue: 'B',
numbers: [0, 31721.57],
partition: {
values: [
{
dimensionValue: '1',
numbers: [0, 0],
partition: {
values: [
{
dimensionValue: 'a',
numbers: [0, 31721.57],
partition: null
}
]
}
}
]
}
}
]
}
const extractDimensionValues = (
partitionValues
) => {
return (
partitionValues.flatMap(({ dimensionValue, numbers, partition }) => {
const mainValues = {
dimensions: [dimensionValue],
values: [...numbers],
}
const nestedValues = partition.values.flatMap((value) => {
return ({
dimensions: [dimensionValue, value.dimensionValue],
values: [...value.numbers]
})
})
return !partition.values ? [mainValues] : [mainValues, ...nestedValues]
})
)
}
const data = [...extractDimensionValues(partition.values)]
console.log('data :', data)

I updated the code so it flattens the partitionValues array and map over it to extract the relevant dimension values and corresponding numbers. The extracted data is stored in a new array data which is a one-dimensional array.
Updated code:
const partition = {
values: [
{
dimensionValue: 'A',
numbers: [0, 33],
partition: {
values: [
{
dimensionValue: '1',
numbers: [0, 3375],
partition: {
values: [
{
dimensionValue: 'a',
numbers: [0, 0],
partition: null
},
{
dimensionValue: 'b',
numbers: [0, 8],
partition: null
}
]
}
},
{
dimensionValue: '2',
numbers: [6028, 0],
partition: {
values: [
{
dimensionValue: 'a',
numbers: [0, 6028],
partition: null
}
]
}
}
]
}
},
{
dimensionValue: 'B',
numbers: [0, 31721.57],
partition: {
values: [
{
dimensionValue: '1',
numbers: [0, 0],
partition: {
values: [
{
dimensionValue: 'a',
numbers: [0, 31721.57],
partition: null
}
]
}
}
]
}
}
]
};
const extractDimensionValues = (partitionValues, currentDimensions = []) => {
return (
partitionValues.flatMap(({ dimensionValue, numbers, partition }) => {
const dimensions = [...currentDimensions, dimensionValue];
const values = { dimensions, values: [...numbers] };
if (!partition || !partition.values) {
return [values];
}
return [
values,
...extractDimensionValues(partition.values, dimensions)
];
})
);
};
const data = [...extractDimensionValues(partition.values)];
console.log('data:', data);

You could take a recursive approach by using the nested result for mapping with the actual dimensionValue.
const
getFlat = o => o?.values?.flatMap(({ dimensionValue, numbers: values, partition }) => [
{ dimensions: [dimensionValue], values },
...getFlat(partition)
.map(({ dimensions, values }) => ({ dimensions: [dimensionValue, ...dimensions], values }))
]) || [],
partition = { values: [{ dimensionValue: 'A', numbers: [0, 33], partition: { values: [{ dimensionValue: '1', numbers: [0, 3375], partition: { values: [{ dimensionValue: 'a', numbers: [0, 0], partition: null }, { dimensionValue: 'b', numbers: [0, 8], partition: null }] } }, { dimensionValue: '2', numbers: [6028, 0], partition: { values: [{ dimensionValue: 'a', numbers: [0, 6028], partition: null }] } }] } }, { dimensionValue: 'B', numbers: [0, 31721.57], partition: { values: [{ dimensionValue: '1', numbers: [0, 0], partition: { values: [{ dimensionValue: 'a', numbers: [0, 31721.57], partition: null }] } } ] } }] },
flat = getFlat(partition);
console.log(flat);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Another flattening, recursive approach could be based on a single reduce task which does recursively create the new flattened partition value from the currently processed item and keeps track of the correct dimensions path/array by always passing its recent state as part of an accumulating object which also holds the temporary and the final result-array.
function recursivelyCreateAndCollectFlattenedPartitionValue(
// - the destructured initial value and the accumulated value.
{ result = [], dimensions = [] },
// - the destructured currently processed array item.
{ dimensionValue, numbers: values, partition = null },
) {
result
// - push ...
.push({
// ... always the newly created flatened item ...
dimensions: dimensions.concat(dimensionValue),
values,
}, ...(
// ... and, only if necessary, the `result` array
// of the next recursion cycle while "walking the tree".
partition?.values.reduce(
recursivelyCreateAndCollectFlattenedPartitionValue, {
dimensions: dimensions.concat(dimensionValue),
result: [],
},
).result ?? []
));
return { result, dimensions: [...dimensions] };
}
const partition = {
values: [{
dimensionValue: 'A',
numbers: [0, 33],
partition: {
values: [{
dimensionValue: '1',
numbers: [0, 3375],
partition: {
values: [{
dimensionValue: 'a',
numbers: [0, 0],
partition: null,
}, {
dimensionValue: 'b',
numbers: [0, 8],
partition: null,
}],
},
}, {
dimensionValue: '2',
numbers: [6028, 0],
partition: {
values: [{
dimensionValue: 'a',
numbers: [0, 6028],
partition: null,
}],
},
}],
},
}, {
dimensionValue: 'B',
numbers: [0, 31721.57],
partition: {
values: [{
dimensionValue: '1',
numbers: [0, 0],
partition: {
values: [{
dimensionValue: 'a',
numbers: [0, 31721.57],
partition: null,
}],
},
}],
},
}],
};
const { result: listOfFlattenedPartitionValues } = partition
.values
.reduce(recursivelyCreateAndCollectFlattenedPartitionValue, { result: [] });
console.log({ listOfFlattenedPartitionValues });
.as-console-wrapper { min-height: 100%!important; top: 0; }

This is similar to the solution from Filip Huhta, but with no local variables, and in fact, no statements at all, only expressions:
const flatten = ({values}, path = []) => values .flatMap (
({dimensionValue, numbers, partition}) => [
{dimensions: [...path, dimensionValue], values: numbers},
...(partition ? flatten (partition, path .concat (dimensionValue) ): [])
]
)
const partition = {values: [{dimensionValue: "A", numbers: [0, 33], partition: {values: [{dimensionValue: "1", numbers: [0, 3375], partition: {values: [{dimensionValue: "a", numbers: [0, 0], partition: null}, {dimensionValue: "b", numbers: [0, 8], partition: null}]}}, {dimensionValue: "2", numbers: [6028, 0], partition: {values: [{dimensionValue: "a", numbers: [0, 6028], partition: null}]}}]}}, {dimensionValue: "B", numbers: [0, 31721.57], partition: {values: [{dimensionValue: "1", numbers: [0, 0], partition: {values: [{dimensionValue: "a", numbers: [0, 31721.57], partition: null}]}}]}}]}
console .log (flatten (partition))
.as-console-wrapper {max-height: 100% !important; top: 0}
I find this a very clean way to code.

Related

Looping and splitting Object

how to loop on object and split it by ":" into separate one
{
labs: [1,2,":",3,4],
level: [1,2,":",3,4]
}
Expected Output:
{
labs: [1,2],
level: [1,2]
}
{
labs: [3,4],
level : [3,4]
}
You can use itertools.groupby from the standard library to group the numbers that are not ":". With that you can use zip to pair off the groups.
from itertools import groupby
d = {
"labs": [1,2,":",3,4],
"level": [10,20,":",30,40]
}
groups = [[(k, list(g)) for b, g in groupby(v, key=lambda n:n != ':') if b]
for k, v in d.items()
]
list(map(dict, zip(*groups)))
# [
# {'labs': [1, 2], 'level': [10, 20]},
# {'labs': [3, 4], 'level': [30, 40]}
# ]
This should work with arbitrary data. For example with input like:
d = {
"labs": [1,2,":",3,4,":", 5, 6],
"level": [10,20,":",30,40,":",50, 60],
"others":[-1,-2,":",-3,-4,":",-5,-6]
}
You will get:
[{'labs': [1, 2], 'level': [10, 20], 'others': [-1, -2]},
{'labs': [3, 4], 'level': [30, 40], 'others': [-3, -4]},
{'labs': [5, 6], 'level': [50, 60], 'others': [-5, -6]}
]
But it does expect the lists to be the same length because the way zip() works. If that's not a good assumption you will need to decide what to do with uneven lists. In that case itertools.zip_longest() will probably be helpful.
Use javaScript to resolve this problem, maybe the code is not the best
const obj = {
labs: [1,2,":",3,4,":",5,6],
level: [1,2,":",3,4,":",7,8],
others: [1,2,":",3,4,":",9,10]
}
const format = (obj = {}, result = []) => {
const keys = Object.keys(obj);
for ( key of keys) {
const itemValues = obj[key].toString().split(':');
let tempRes = {}
itemValues.map((itemValue, index) => {
Object.assign(tempRes, {
[`${keys[index]}`]: itemValue.replace(/^(,)+|(,)+$/g, '').split(',').map(Number)// you can format value here
})
})
result.push(tempRes);
}
return result;
}
console.log(format(obj))
You will get
[
{ labs: [ 1, 2 ], level: [ 3, 4 ], others: [ 5, 6 ] },
{ labs: [ 1, 2 ], level: [ 3, 4 ], others: [ 7, 8 ] },
{ labs: [ 1, 2 ], level: [ 3, 4 ], others: [ 9, 10 ] }
]

manipulate array of hashes using javascript

Can you please show me the best way to process/ manipulate this array of hashes using javascript function chaining. I want to filter the data and return ONLY an array of hashes that contains data that in NOT all 0’s.
data = [
{name: "apple", data: [0, 0, 0]},
{name: "banana", data: [1, 0, 2]},
{name: "carrot", data: [0, 0, 0]},
{name: "pineapple", data: [0, 0, 3]},
]
//result after filtering
data = [
{name: "banana", data: [1, 0, 2]},
{name: "pineapple", data: [0, 0, 3]},
]
I was thinking something along the lines of
data.filter((hash,i) => {hash.data.every((elem,i)=> elem == 0);
This should do it
const data = [{name:”apple”,data:[0,0,0]}, {name:”banana”,data:[1,0,2]}, {name:””,carrot:[0,0,0]}, {name:”pineapple”,data:[0,0,3]}, ]
const nonzero = data.filter(({data}) => data.some(Boolean));
You were almost right. You need to invert the result of the every test, since you want the elements that aren't all zero; you could also use some and test for non-zero. Also, don't put curly braces around the body of the function if you just want to return the result of the expression.
data = [{
name: "apple",
data: [0, 0, 0]
},
{
name: "banana",
data: [1, 0, 2]
},
{
name: "carrot",
data: [0, 0, 0]
},
{
name: "pineapple",
data: [0, 0, 3]
},
];
console.log(data.filter(hash => !hash.data.every(elem => elem == 0)));

Matching a set of items containing arrays in MongoDB

Let's say I have the following data
[
{ name: 'A', values: [1, 2, 3 ,4] },
{ name: 'B', values: [5, 6, 7, 8] }
]
and I have a mongodb that has the following items in a collection:
#1 { name: 'A', values: [1, 2, 3 ,4], path: '...' },
#2 { name: 'B', values: [8, 9, 5, 7], path: '...' },
#3 { name: 'B', values: [5, 6, 7, 8], path: '...' },
#4 { name: 'C', values: [9, 10, 11, 12], path: '...' }
Now I want to query the paths that match my 2 items A and B from the data (Items #1 and #3). Is that possible edit: with a single query?
You can loop through the data and use query inside the map function by making it asynchronous
const data = [
{ name: 'A', values: [1, 2, 3 ,4] },
{ name: 'B', values: [5, 6, 7, 8] }
]
const result = []
await Promise.all(data.map(async(d) => {
result.push(await db.collection.findOne(d))
}))
Or even with the single query
await db.collection.find(
{
name: { $in: data.map(({ name }) => name) },
values: { $in: data.map(({ values }) => values) }
}
)

Numeric sort on an array with javascript

I have this array :
[ [ 1, 'a' ], [ 2, 'b' ], [ 1, 'd' ], [ 9, 'e' ], [ 1, 'f' ], [ 11, 'g' ], [ 9, 'h' ], [ 3, 'i' ] ]
and I would like to have :
[ [ 11, 'g' ], [ 9, 'e' ], [ 9, 'h' ], [ 3, 'i' ], [ 2, 'b' ], [ 1, 'a' ], [ 1, 'd' ], [ 1, 'f' ] ]
How can I do that with javascript please ?
I tried sort(), I also tried with sort(compare) with :
function compare(x, y) {
return x - y;
}
You can use .sort() with Array Destructuring like this:
function compare([a], [b]) {
return b - a;
}
Demo:
let a = [ [ 1, 'a' ], [ 2, 'b' ], [ 1, 'd' ], [ 9, 'e' ], [ 1, 'f' ], [ 11, 'g' ], [ 9, 'h' ], [ 3, 'i' ] ];
a.sort(compare);
function compare([a], [b]) {
return b - a;
}
console.log(a);
.as-console-wrapper { max-height: 100% !important; top: 0; }
In case first elements match, you can sort based on 2nd element as well:
function compare([a, c], [b, d]) {
return (b - a) || c.localeCompare(d)
}
Demo:
let a = [ [ 2, 'b' ], [ 1, 'd' ], [ 9, 'e' ], [ 1, 'f' ], [ 11, 'g' ], [ 9, 'h' ], [ 3, 'i' ], [ 1, 'a' ] ];
a.sort(compare);
function compare([a, c], [b, d]) {
return (b - a) || c.localeCompare(d);
}
console.log(a);
.as-console-wrapper { max-height: 100% !important; top: 0 }
You need to compare the first element in the nested array since you want to sort based on that number.
function compare(x, y) {
return y[0] - x[0];
}
var data = [
[1, 'a'],
[2, 'b'],
[1, 'd'],
[9, 'e'],
[1, 'f'],
[11, 'g'],
[9, 'h'],
[3, 'i']
];
function compare(x, y) {
return y[0] - x[0];
}
data.sort(compare);
console.log(data);
In case you want to sort based on second element(secondary sorting in case the first element is same) then use String#localeCompare method for comparing.
function compare(x, y) {
return y[0] - x[0] || x[1].localeCompare(y[0]);
}
var data = [
[2, 'b'],
[1, 'd'],
[9, 'e'],
[1, 'f'],
[1, 'a'],
[11, 'g'],
[9, 'h'],
[3, 'i']
];
function compare(x, y) {
return (y[0] - x[0]) || x[1].localeCompare(y[1]);
}
data.sort(compare);
console.log(data);
Compare the elements based on their first element, which is the number.
var a = [ [ 1, 'a' ], [ 2, 'b' ], [ 1, 'd' ], [ 9, 'e' ], [ 1, 'f' ], [ 11, 'g' ], [ 9, 'h' ], [ 3, 'i' ] ];
a = a.sort((a,b) => {
return b[0] - a[0]
});
console.log(a)
Use sort to compare first element and if first element is same then compare the second element.
arr.sort( (a,b) => (b[0] - a[0]) || (b[1] - a[1]) )
Demo
var arr = [ [ 1, 'a' ], [ 2, 'b' ], [ 1, 'd' ], [ 9, 'e' ], [ 1, 'f' ], [ 11, 'g' ], [ 9, 'h' ], [ 3, 'i' ] ];
arr.sort( (a,b) => (b[0] - a[0]) || (b[1] - a[1]) );
console.log(arr);

How can I rewrite python code to javascript?

The code in python is:
def trimTree(tree):
p=tree[1]
if type(p) == type(""): return p
else :
return(trimTree(p[0]),trimTree(p[1]))
where tree is:
[
13,
[ 6, [ 3, [Object], [Object] ], [ 3, 'a' ] ],
[ 7, [ 3, 'b' ], [ 4, [Object], [Object] ] ]
]
when I convert I got error:
TypeError: Cannot read property '0' of undefined
What should I do?
With a proper data structure, which means any node has only a length of two elements, you get a list of values in breaths first order (the result is here a string).
function trimTree(tree) {
var p = tree[1];
return typeof p === 'string'
? p
: trimTree(p[0]) + trimTree(p[1]);
}
var data = [
13,
[
[6,
[
[3,
[
[1, 'X'],
[2, 'Y']
]
],
[3, 'a']
]
],
[7,
[
[3, 'b'],
[4,
[
[2, 'Z'],
[2, 'Q']
]
]
]
]
]
];
console.log(trimTree(data));

Categories