transform array with map and reduce - javascript

I have several arrays like that I'm getting from a web service:
const a = [ { label: 'A', value: 100 }, { label: 'B', value: 200 } ];
const b = [ { label: 'A', value: 50 }, { label: 'B', value: 300 } ];
const c = [ { label: 'A', value: 20 }, { label: 'B', value: 130 } ];
const d = [ { label: 'A', value: 10 }, { label: 'B', value: 25 } ];
and I would like to have something like that (in a React state):
[
{ label: 'A', a: 100, b: 50, c: 20, d: 10 },
{ label: 'B', a: 200, b: 300, c: 130, d: 25 }
]
using modern JavaScript, I guess with map and reduce
EDIT:
I was not clear at all at first. I want to update my state when I get new data.
if my current state is:
[
{ label: 'A', a: 100, b: 50, c: 20, d: 10 },
{ label: 'B', a: 200, b: 300, c: 130, d: 25 }
]
and I'm getting
{ title: "a", values: [ { label: 'A', value: 12 }, { label: 'B', value: 13 } ] };
I want to update my state to
[
{ label: 'A', a: 12, b: 50, c: 20, d: 10 },
{ label: 'B', a: 13, b: 300, c: 130, d: 25 }
]
my best attempts was:
myFunction().then(data => {
const {chartData} = this.state;
chartData[data.title] = Object.keys(data.values).map(key => ({
label: chartData[data.title] || data.values[key].label,
[data.title]: data.values[key].value,
...chartData[data.title]
});
this.setState({chartData});
})

You could iterate the given state array, look for an item with the wanted label and update the properties.
function update(state, { title, values }) {
return values.reduce((r, { label, value }) => {
var temp = r.find(o => o.label === label);
if (!temp) r.push(temp = { label });
Object.assign(temp, { [title]: value });
return r;
}, state);
}
var state = [{ label: 'A', a: 100, b: 50, c: 20, d: 10 }, { label: 'B', a: 200, b: 300, c: 130, d: 25 }],
data = { title: "a", values: [{ label: 'A', value: 12 }, { label: 'B', value: 13 }] };
update(state, data);
console.log(state);
.as-console-wrapper { max-height: 100% !important; top: 0; }

const glue = (label, ...vars) => ([].concat(vars).filter(i => i.label === label).reduce((agg, i) => ({ ...agg, ...i }), {}));
where
glue('A', [a,b,c,d]);
glue('B', [a,b,c,d]);
// .. and so on
`

Create an object with all the arrays using Shorthand property names (The key names are required to create the properties in the output. You could add more arrays to this object). Then reduce the entries returned by Object.entries()
const a = [ { label: 'A', value: 100 }, { label: 'B', value: 200 } ];
const b = [ { label: 'A', value: 50 }, { label: 'B', value: 300 } ];
const c = [ { label: 'A', value: 20 }, { label: 'B', value: 130 } ];
const d = [ { label: 'A', value: 10 }, { label: 'B', value: 25 } ];
const input = { a, b, c, d };
const merged = Object.entries(input).reduce((r, [key, arr]) => {
arr.forEach(({label, value}) => {
r[label] = r[label] || { label };
r[label][key] = value;
})
return r;
}, {})
console.log(Object.values(merged))

Here idea is
Combine all the values in one array
Use a array to keep alphabets
Loop over combined array and for each element in combined array add vales to labels and alphabet based as per index of combined array
const a = [{ label: 'A', value: 100 }, { label: 'B', value: 200 }];
const b = [{ label: 'A', value: 50 }, { label: 'B', value: 300 }];
const c = [{ label: 'A', value: 20 }, { label: 'B', value: 130 }];
const d = [{ label: 'A', value: 10 }, { label: 'B', value: 25 }];
let alpha = [...'abcdefghijklmnopqrstuvwxyz']
let combine = [a,b,c,d]
let op = combine.reduce((op,inp,i) => {
inp.forEach(({label,value}) => {
op[label] = op[label] || {label}
op[label][alpha[i]] = value
})
return op
},{})
console.log(Object.values(op))

Related

reducing nested object key value inside an an array

i have the following arrays
array1 = [
{a:{key:1 , value: 10} , b:{key:1 , value:12} , c:{key:1 , value: 5} , d:{key:1 , value:2}},
{a:{key:2 , value: 10} , b:{key:2 , value:12} , c:{key:2 , value: 5} , d:{key:2 , value:2}},
{a:{key:3 , value: 10} , b:{key:3 , value:12} , c:{key:3 , value: 5} , d:{key:3 , value:2}},
]
array2 = [
{a:{key:1 , value: 10} , b:{key:1 , value:12} , c:{key:1 , value: 5} , d:{key:1 , value:2}},
{a:{key:2 , value: 10} , b:{key:2 , value:12} , c:{key:2 , value: 5} , d:{key:2 , value:2}},
{a:{key:4 , value: 10} , b:{key:4 , value:12} , c:{key:4 , value: 5} , d:{key:4 , value:2}},
]
reduced array based on key should look like this:
combinedArray= [
{a:{key:1 , value: 20} , b:{key:1 , value:24} , c:{key:1 , value: 10} , d:{key:1 , value:4}},
{a:{key:2 , value: 20} , b:{key:2 , value:24} , c:{key:2 , value: 10} , d:{key:2 , value:4}},
{a:{key:3 , value: 10} , b:{key:3 , value:12} , c:{key:3 , value: 5} , d:{key:3 , value:2}},
{a:{key:4 , value: 10} , b:{key:4 , value:12} , c:{key:4 , value: 5} , d:{key:4 , value:2}},
]
first i tried to merge the two arrays using const mergedArray = [...array1, ...array2]
now i want to check for key duplicates. for example, if there is key1 in both array1 and array2, remove the duplicates then combine the values of that key.
this is what i have tried but it is only iterating through a.key only:
function kdeAdder(param) {
const array = [param.a]
let tempHistory = [];
for(let x=0;x<array.length;x++){
array[x].forEach((item)=>{
let noMatch = true;
if(tempHistory.length > 0) {
tempHistory.forEach((tempItem, i)=>{
if(item.key === tempItem.key) {
tempHistory[i].value += item.value;
noMatch = !noMatch;
}
});
}
return (noMatch) ? tempHistory.push(item) : null;
});
}
return tempHistory;
}
kdeAdder(mergedArray);
As you confirmed the key inner property is commonly shared by the four "a", "b", "c", "d" objects in an outer object, the a.key value can be used to identify which outer objects should merge.
You could group all objects (irrespective of whether they occur in array1 or array2) by that a.key, and then aggregate objects that occur in the same group. Both of these actions can be accomplished with a reduce call:
const aggregate = (objects) =>
objects.reduce((x, y) => ({
a: { key: x.a.key, value: x.a.value + y.a.value },
b: { key: x.b.key, value: x.b.value + y.b.value },
c: { key: x.c.key, value: x.c.value + y.c.value },
d: { key: x.d.key, value: x.d.value + y.d.value },
}));
const merge = (array1, array2) =>
Object.values(array1.concat(array2).reduce((acc, obj) => {
(acc[obj.a.key] ??= []).push(obj);
return acc;
}, {})).map(aggregate);
const array1 = [
{a:{key:1 , value: 10} , b:{key:1 , value:12} , c:{key:1 , value: 5} , d:{key:1 , value:2}},
{a:{key:2 , value: 10} , b:{key:2 , value:12} , c:{key:2 , value: 5} , d:{key:2 , value:2}},
{a:{key:3 , value: 10} , b:{key:3 , value:12} , c:{key:3 , value: 5} , d:{key:3 , value:2}},
];
const array2 = [
{a:{key:1 , value: 10} , b:{key:1 , value:12} , c:{key:1 , value: 5} , d:{key:1 , value:2}},
{a:{key:2 , value: 10} , b:{key:2 , value:12} , c:{key:2 , value: 5} , d:{key:2 , value:2}},
{a:{key:4 , value: 10} , b:{key:4 , value:12} , c:{key:4 , value: 5} , d:{key:4 , value:2}},
]
console.log(merge(array1, array2));
You can first reduce the output to a single object since its a sort of accumulation of numbers, and then get the format you want as the second step.
const array1 = [ { a: { key: 1, value: 10 }, b: { key: 1, value: 12 }, c: { key: 1, value: 5 }, d: { key: 1, value: 2 }, }, { a: { key: 2, value: 10 }, b: { key: 2, value: 12 }, c: { key: 2, value: 5 }, d: { key: 2, value: 2 }, }, { a: { key: 3, value: 10 }, b: { key: 3, value: 12 }, c: { key: 3, value: 5 }, d: { key: 3, value: 2 }, }, ]; const array2 = [ { a: { key: 1, value: 10 }, b: { key: 1, value: 12 }, c: { key: 1, value: 5 }, d: { key: 1, value: 2 }, }, { a: { key: 2, value: 10 }, b: { key: 2, value: 12 }, c: { key: 2, value: 5 }, d: { key: 2, value: 2 }, }, { a: { key: 4, value: 10 }, b: { key: 4, value: 12 }, c: { key: 4, value: 5 }, d: { key: 4, value: 2 }, }, ];
const mergedArray = [...array1, ...array2];
const keys = []
const reducedOutput = mergedArray.reduce((prev, curr) => {
Object.entries(curr).forEach(([mainKey, { key, value }]) => {
// mainKey is a, b, c, d in your case
if (!prev[mainKey]) {
prev[mainKey] = {};
}
// key is 1, 2, 3, 4 in your case
if (!keys.includes(key)) {
keys.push(key)
}
prev[mainKey][key] = prev[mainKey][key]
? prev[mainKey][key] + value
: value;
});
return prev;
}, {});
const output = keys.map(key => {
const obj = {}
Object.entries(reducedOutput).forEach(([k, v]) => {
obj[k] = {key, value: v[key]}
})
return obj
})
console.log(output)
This will work with any other keys for a, b, c, d keys and 1, 2, 3, 4 keys you have used in two levels.
Using Object.entries(), Array.prototype.reduce(), Array.prototype.forEach(), and Array.prototype.map()
The following provided code implements an approach which tries to find a balance in between 1st being agnostic to any array item's current and future structure except for both property names, key and value, of any array item's second level structure and 2nd how to handle the merger of other unknown second level data.
Therefore the general approach creates an object based lookup from the shorter sourceList where each item gets referred to via the value of its second level key property, whereas the longer targetList will be reduced in order to create the final result of merged items from both arrays.
Since approach and implementation are unaware of an items first level structure, one has to reduce again all of a currently processed item's entries. For each of a target item's unknown entry one can rely on such an entry's 2nd level properties, key and value. From all the available data, either known or unknown, one can aggregate the common merger of both the source- and the target-item; their values will be totaled and both of their unknown rest data will be merged by spread syntax, where the latter is the approach's trade off/compromise.
function aggregateFirstValueKeyBasedLookup(lookup, item) {
lookup[Object.values(item)[0]?.key ?? ''] = item;
return lookup;
}
function createKeyBasedValueMergerFromSourceLookup(
{ lookup = {}, result = [] }, targetItem, idx, arr,
) {
let currentLookupKey;
result.push(Object
.entries(targetItem)
.reduce((merger, [
targetEntryKey, {
key, value: targetEntryValue = 0, ...targetEntryRest
}
]) => {
currentLookupKey = key;
const sourceItem = lookup[key] ?? {};
const {
value: sourceEntryValue = 0, ...sourceEntryRest
} = sourceItem[targetEntryKey] ?? {};
return Object.assign(merger, {
[ targetEntryKey ]: {
key,
value: (targetEntryValue + sourceEntryValue),
...targetEntryRest,
...sourceEntryRest,
},
});
}, {})
);
// delete already processed source-items from lookup.
Reflect.deleteProperty(lookup, currentLookupKey);
if (idx >= arr.length - 1) {
// finalize the result by ...
result.push(
// ...pushing all of the lookup's
// unprocessed source-items.
...[...Object.values(lookup)]
);
}
return { lookup, result };
}
const array1 = [{
a: { key: 1, value: 10 }, b: { key: 1, value: 12 }, c: { key: 1, value: 5 }, d: { key: 1, value: 2 }
}, {
a: { key: 2, value: 10 }, b: { key: 2, value: 12 }, c: { key: 2, value: 5 }, d: { key: 2, value: 2 }
}, {
a: { key: 3, value: 10 }, b: { key: 3, value: 12 }, c: { key: 3, value: 5 }, d: { key: 3, value: 2 }
}];
const array2 = [{
a: { key: 1, value: 10 }, b: { key: 1, value: 12 }, c: { key: 1, value: 5 }, d: { key: 1, value: 2 }
}, {
a: { key: 2, value: 10 }, b: { key: 2, value: 12 }, c: { key: 2, value: 5 }, d: { key: 2, value: 2 }
}, {
a: { key: 4, value: 10 }, b: { key: 4, value: 12 }, c: { key: 4, value: 5 }, d: { key: 4, value: 2 }
}];
const [ targetList, sourceList ]
= [array1, array2].sort((a, b) => b.length - a.length);
const sourceLookup = sourceList
.reduce(aggregateFirstValueKeyBasedLookup, Object.create(null));
console.log({ sourceLookup });
const { result: mergedItemList } = targetList
.reduce(createKeyBasedValueMergerFromSourceLookup, {
lookup: sourceLookup, result: [],
});
console.log({ mergedItemList });
// - changed item structure which keeps
// just the most necessary pattern.
const newItemStructureList1 = [{
quick: { key: 'foo', value: 33, biz: 'biz' },
brown: { key: 'foo', value: 22, baz: 'baz' },
fox: { key: 'foo', value: 11, buzz: 'buzz' },
}, {
quick: { key: 'bar', value: 11, baz: 'baz' },
brown: { key: 'bar', value: 33, biz: 'biz' },
fox: { key: 'bar', value: 22, booz: 'booz' },
}, {
quick: { key: 'baz', value: 22, baz: 'baz' },
brown: { key: 'baz', value: 11, biz: 'biz' },
fox: { key: 'baz', value: 33, booz: 'booz' },
}];
const newItemStructureList2 = [{
brown: { key: 'foo', value: 11, baz: 'baz' },
fox: { key: 'foo', value: 33, booz: 'booz' },
quick: { key: 'foo', value: 22, baz: 'baz' },
}, {
fox: { key: 'baz', value: 33, buzz: 'buzz' },
quick: { key: 'baz', value: 11, biz: 'biz' },
brown: { key: 'baz', value: 33, baz: 'baz' },
}];
const [ target, source ]
= [newItemStructureList1, newItemStructureList2].sort((a, b) => b.length - a.length);
const lookup = source
.reduce(aggregateFirstValueKeyBasedLookup, Object.create(null));
console.log({ lookup });
const { result: mergedItems } = target
.reduce(createKeyBasedValueMergerFromSourceLookup, { lookup, result: [] });
console.log({ mergedItems });
.as-console-wrapper { min-height: 100%!important; top: 0; }
You could group with the key property and the outer property.
const
array1 = [{ a: { key: 1, value: 10 }, b: { key: 1, value: 12 }, c: { key: 1, value: 5 }, d: { key: 1, value: 2 } }, { a: { key: 2, value: 10 }, b: { key: 2, value: 12 }, c: { key: 2, value: 5 }, d: { key: 2, value: 2 } }, { a: { key: 3, value: 10 }, b: { key: 3, value: 12 }, c: { key: 3, value: 5 }, d: { key: 3, value: 2 } }],
array2 = [{ a: { key: 1, value: 10 }, b: { key: 1, value: 12 }, c: { key: 1, value: 5 }, d: { key: 1, value: 2 } }, { a: { key: 2, value: 10 }, b: { key: 2, value: 12 }, c: { key: 2, value: 5 }, d: { key: 2, value: 2 } }, { a: { key: 4, value: 10 }, b: { key: 4, value: 12 }, c: { key: 4, value: 5 }, d: { key: 4, value: 2 } }],
result = Object.values([...array1, ...array2].reduce((r, o) => {
Object.entries(o).forEach(([k, { key, value }]) => {
r[key] ??= {};
r[key][k] ??= { key, value: 0 };
r[key][k].value += value;
});
return r;
}, {}));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

How to transform an object into an array of objects by its keys?

I've got a data source:
const data = {
A: [{
value: 1
}, {
value: 2
}, {
value: 38
}],
B: [{
value: 46
}, {
value: 23
}, {
value: 32
}],
C: [{
value: 2345
}, {
value: 56
}, {
value: 3
}]
}
I need to transform this object in an array of objects like below:
[{
A: 1,
B: 46,
C: 2345
}, {
A: 2,
B: 23,
C: 56
}, {
A: 38,
B: 32,
C: 3
}]
I made some attempts but still not there:
a)
const result = Object.keys(data).map(key => data[key])
b)
const b = Object.keys(data).reduce((acc, curr, i) => {
const values = data[curr].map(el => el.value)
acc[curr] = values[i]
return acc
}, [])
EDIT:
All arrays (A, B, C) should have the same length. In case it doesn't, value should be "-" for the missing ones
eg:
[{
A: 1,
B: 46,
C: 2345
}, {
A: 2,
B: 23,
C: 56
}, {
A: 38,
B: 32,
C: -
}]
Assuming the number of objects in your value arrays is the same (or less than) the number of properties in your data object, you could map your data keys to new objects that you can create using Object.fromEntries(). You can pass an array of mapped values to this fromEntries call, which goes through all your keys a, b, c and uses the current index from the outer loop to determine which object to grab its value from:
const data = { A: [{ value: 1 }, { value: 2 }, { value: 38 }], B: [{ value: 46 }, { value: 23 }, { value: 32 }], C: [{ value: 2345 }, { value: 56 }, { value: 3 }] };
const res = Object.keys(data).map((_, i, arr) =>
Object.fromEntries(arr.map(key => [key, data[key][i]?.value ?? "-"]))
);
console.log(res);
If you need a more robust approach, I suggest you go with a solution such as T.J. Crowder's that can handle the case where there are more objects in your arrays than there are properties in your object.
I'm going to assume that the fact there are three properties (A, B, and C) and the fact there are three values for each of them is a coincidence and that we can't rely on that.
If so, see comments:
// Get the keys
const keys = Object.keys(data);
// A blank object to use as a template for a new array entry w/blank values
const blankEntry = Object.fromEntries(keys.map(key => [key, "-"]));
// Create the result array; since we don't know how long it'll need to
// be without reading through the array, just start with a blank one)
const result = [];
// Loop through the properties
for (const key of keys) {
// Loop through this property's values
const values = data[key];
for (let index = 0; index < values.length; ++index) {
// Get the object at this index, creating it if not there
let entry = result[index];
if (!entry) {
// Make a shallow copy of the template to create the entry
result[index] = entry = {...blankEntry};
}
// Set this key's value
entry[key] = values[index].value;
}
}
Live Example (I've added a fourth entry to A to show that the three and three thing isn't important to the solution, and to show the "-" thing working):
const data = {
A: [{
value: 1
}, {
value: 2
}, {
value: 38
}, {
value: 42
}],
B: [{
value: 46
}, {
value: 23
}, {
value: 32
}],
C: [{
value: 2345
}, {
value: 56
}, {
value: 3
}]
};
const keys = Object.keys(data);
const blankEntry = Object.fromEntries(keys.map(key => [key, "-"]));
const result = [];
for (const key of keys) {
const values = data[key];
for (let index = 0; index < values.length; ++index) {
let entry = result[index];
if (!entry) {
result[index] = entry = {...blankEntry};
}
entry[key] = values[index].value;
}
}
console.log(result);
.as-console-wrapper {
max-height: 100% !important;
}
You could get the max length first and then map the values.
const
data = { A: [{ value: 1 }, { value: 2 }, { value: 38 }, { value: 99 }], B: [{ value: 46 }, { value: 23 }, { value: 32 }], C: [{ value: 2345 }, { value: 56 }, { value: 3 }] },
entries = Object.entries(data),
length = entries.reduce((r, [, { length }]) => Math.max(r, length), 0),
result = entries.reduce(
(r, [k, a]) => r.map((o, i) => ({ ...o, ...(i in a && { [k]: a[i].value }) })),
Array.from(
{ length },
() => Object.fromEntries(entries.map(([k]) => [k, '-']))
)
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can create an empty object with - and assign value after with reduce() and forEach().
const data = { A: [ { value: 1, }, { value: 2, }, { value: 38, }, ], B: [ { value: 46, }, { value: 23, }, { value: 32, }, ], C: [ { value: 2345, }, { value: 56, }, { value: 3, }, ], };
const keys = Object.keys(data);
const emptyObj = keys.map(_ =>
keys.reduce((o, key) => ({ ...o, [key]: "-" }), {})
);
const o = keys.reduce((a, b) => {
data[b].forEach((el, i) => {
a[i][b] = el.value;
});
return a;
}, emptyObj);
console.log(o);
I think I'd try with something like:
// input
const data = {
A: [{ value: 1 }, { value: 2 }, { value: 38 }, { value: 57 }],
B: [{ value: 46 }, { value: 23 }, { value: 32 }, { value: 42 }],
C: [{ value: 2345 }, { value: 56 }, { value: 3 }],
D: [{ value: 345 }, { value: 684 }]
};
// I am using an IIFE to initialize `output` in order not to pollute the global scope
// with variables that are not re-used anywhere else but obviously it is not strictly necessary.
const output = (() => {
const keys = Object.keys(data);
const maxItemsNr = Math.max(...Object.values(data).map(item => item.length));
const newArr = [];
for (let i = 0; i < maxItemsNr; i++) {
const item = keys.reduce((outObj, key) => {
outObj[key] = data[key][i] ? data[key][i].value : '-';
return outObj;
}, {});
newArr.push(item);
}
return newArr
})();
// test
console.log(output);
And this is my take on it:
const data = { A: [{value: 1}, {value: 2}],
B: [{value: 46}, {value: 23}, {value: 32}, {value: 38}, {value: 42}],
C: [{value: 2345}, {value: 56}, {value: 3}] };
const ntrs = Object.entries(data),
blnk = ntrs.reduce((a,[key])=>(a[key]="-",a),{}),
rslt = [];
ntrs.forEach(([k,arr])=> arr.forEach(({value},i)=>(rslt[i]=rslt[i]||{...blnk})[k]=value) )
console.log(rslt);
.as-console-wrapper {
max-height: 100% !important;
}
My solution. But I think Nick Parson's solution is better.
const data = {A: [{value: 1}, {value: 2}, {value: 38}],B: [{value: 46}, {value: 23}, {value: 32}],C: [{value: 2345}, {value: 56}, {value: 3}]}
function transform(object) {
const res = [];
Object.keys(object).forEach((key)=> {
object[key].forEach(({value},i) => {
if(res[i]) {
res[i][key] = value
} else {
res[i] = {
[key]: value
}
}
})
})
return res
}
console.log(transform(data))

How to get the combination of array values from nested arrays in an array of objects

I have an array of objects with the following structure:
var varientSections = [
{
type: "frame",
values: ["black", "white", "wood"]
},
{
type: "finish",
values: ["matte", "glossy"]
}
];
I want to get the combination of the array values and create a new list with it. Right now, I am able to retrieve the combination from the nested array values using the method called getCombination(varientSections). However, I do not know how to create a new list with the following structure:
var results = [
{
attributes: [
{
type: "frame",
value: "black"
},
{
type: "finish",
value: "matte"
}
]
},
{
attributes: [
{
type: "frame",
value: "black"
},
{
type: "finish",
value: "glossy"
}
]
},
{
attributes: [
{
type: "frame",
value: "white"
},
{
type: "finish",
value: "matte"
}
]
},
{
attributes: [
{
type: "frame",
value: "white"
},
{
type: "finish",
value: "glossy"
}
]
},
{
attributes: [
{
type: "frame",
value: "wood"
},
{
type: "finish",
value: "matte"
}
]
},
{
attributes: [
{
type: "frame",
value: "wood"
},
{
type: "finish",
value: "glossy"
}
]
}
];
Below is my code:
function getCombinations(arr) {
if (arr.length === 0) {
return [[]];
}
let [current, ...rest] = arr;
let combinations = getCombinations(rest);
var result = current.values.reduce(
(accumulator, currentValue) => [
...accumulator,
...combinations.map(c => [currentValue, ...c])
],
[]
);
console.log("result is ");
console.log(result);
return result;
}
let varientCombinations = getCombinations(varientSections);
console.log(varientCombinations);
let updatedVarientDetails = [];
varientSections.forEach((varientSection, index) => {
let type = varientSection.type;
varientCombinations.forEach(combination => {
let obj = [
{
type: type,
value: combination[index]
},
];
updatedVarientDetails.push(obj);
});
});
console.log(updatedVarientDetails);
You could get the cartesian product and give it later the wanted style. The names and values are taken form the handed over object.
The algorithm takes all key/value pairs and has a stric view to the values, that means if an array is found or an object, hence w && typeof w === "object", the actual part is taken an used for adding additional key/value pairs.
For example a small object with two properties
{ a: 1, b: [2, 3] }
yields
[
{ a: 1, b: 2 },
{ a: 1, b: 3 }
]
A bit more advanced object, like
{ a: 1, b: { c: { d: [2, 3], e: [4, 5] } } }
yields the same structure as given
[
{
a: 1,
b: {
c: { d: 2, e: 4 }
}
},
{
a: 1,
b: {
c: { d: 2, e: 5 }
}
},
{
a: 1,
b: {
c: { d: 3, e: 4 }
}
},
{
a: 1,
b: {
c: { d: 3, e: 5 }
}
}
]
Thant means, from any found sub object the cartesian product is taken and combined with the actual values.
const
getCartesian = object => Object.entries(object).reduce(
(r, [key, value]) => {
let temp = [];
r.forEach(s =>
(Array.isArray(value) ? value : [value]).forEach(w =>
(w && typeof w === "object" ? getCartesian(w) : [w]).forEach(x =>
temp.push({ ...s, [key]: x })
)
)
);
return temp;
},
[{}]
),
data = [{ type: "frame", value: ["black", "white", "wood"] }, { type: "finish", value: ["matte", "glossy"] }],
result = getCartesian(data)
.map(o => ({ attributes: Object.assign([], o).map(({ ...o }) => o) }));
console.log(result);
console.log(getCartesian({ a: 1, b: { c: { d: [2, 3], e: [4, 5] } } }));
.as-console-wrapper { max-height: 100% !important; top: 0; }
You could simplify it to this:
var variantSections = [
{
type: "frame",
values: ["black", "white", "wood"]
},
{
type: "finish",
values: ["matte", "glossy"]
}
];
// iterate through each variantSection and create objects like {"type": "frame", "value": "black"}
var sections = variantSections.map(variant => {
return variant.values.map(val => ({type: variant.type, value: val}))
});
// then iterate through the two resulting arrays of objects, combining each into the attributes object you want
var results = [];
for (var i = 0; i < sections[0].length; i++) {
for (var j = 0; j < sections[1].length; j++) {
results.push({attributes: [sections[0][i], sections[1][j]]});
}
}
console.log(JSON.parse(JSON.stringify(results)));

How to find avg in crossfilter group in most efficient way

I want to find the average in the data by grouping similar things in the data.
For example I have data in an object like this:
var data = [
{ key: 'A', value: 1 },
{ key: 'A', value: 6 },
{ key: 'B', value: 8 },
{ key: 'B', value: 9 },
{ key: 'A', value: 7 },
{ key: 'A', value: 6 },
{ key: 'C', value: 8 },
{ key: 'A', value: 9 }
];
How do I find average key-wise without recalculating the average at each step (because I don't know the count at once)?
If I want it in one iteration what is the best way?
You could use a hash table for the result object and for additional information, like sum and count.
In every loop update the result set with new values and the average.
var data = [{ key: 'A', value: 1 }, { key: 'A', value: 6 }, { key: 'B', value: 8 }, { key: 'B', value: 9 }, { key: 'A', value: 7 }, { key: 'A', value: 6 }, { key: 'C', value: 8 }, { key: 'A', value: 9 }],
hash = Object.create(null),
result = [];
data.forEach(function (o) {
if (!hash[o.key]) {
hash[o.key] = { _: { key: o.key, avg: 0 }, sum: 0, count: 0 };
result.push(hash[o.key]._);
}
hash[o.key].sum += o.value;
hash[o.key].count++;
hash[o.key]._.avg = hash[o.key].sum / hash[o.key].count;
});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Sort array of arrays with documents using lodash

I've an array with arrays of documents in it, can some help me on how to sort those arrays with multiple documents inside the array. The user can chose of the type to be searched on. If they can choose 'A' sort all the array items by value associated with type 'A', similarly for the other types. What is the best way to do this. Lodash solution would be highly beneficial. Those values can be integers/strings/guids/datetime fields.
Type is always a string, in any case I don't think it does really matter.
[
[{type:A, value:10},{type:B, value:20},{type:C, value:15},{type:D, value:20}],
[{type:A, value:5},{type:B, value:10},{type:C, value:35},{type:D, value:40}],
[{type:A, value:30},{type:B, value:30},{type:C, value:25},{type:D, value:30}],
[{type:A, value:20},{type:B, value:50},{type:C, value:55},{type:D, value:10}]
]
Desired out put if sorted by type 'A'
[[{type:A, value:5},{type:B, value:10},{type:C, value:35},{type:D, value:40}],
[{type:A, value:10},{type:B, value:20},{type:C, value:15},{type:D, value:20}],
[{type:A, value:20},{type:B, value:50},{type:C, value:55},{type:D, value:10}],
[{type:A, value:30},{type:B, value:30},{type:C, value:25},{type:D, value:30}]]
Here's a lodash solution that uses sortBy for sorting and find to get the corresponding values with a specific type.
var result = _.sortBy(data, function(item) {
return _.find(item, { type: 'A' }).value;
});
var data = [
[{
type: 'A',
value: 10
}, {
type: 'B',
value: 20
}, {
type: 'C',
value: 15
}, {
type: 'D',
value: 20
}],
[{
type: 'A',
value: 5
}, {
type: 'B',
value: 10
}, {
type: 'C',
value: 35
}, {
type: 'D',
value: 40
}],
[{
type: 'A',
value: 30
}, {
type: 'B',
value: 30
}, {
type: 'C',
value: 25
}, {
type: 'D',
value: 30
}],
[{
type: 'A',
value: 20
}, {
type: 'B',
value: 50
}, {
type: 'C',
value: 55
}, {
type: 'D',
value: 10
}]
];
var result = _.sortBy(data, function(item) {
return _.find(item, { type: 'A' }).value;
});
console.log(result);
body > div { min-height: 100%; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>
In plain Javascript, you could look for the given type and sort by the corresponding value.
function sort(data, type) {
function getValue(array) {
var value = -Number.MAX_VALUE;
array.some(function (o) {
if (o.type === type) {
value = o.value;
return true;
}
});
return value;
}
data.sort(function (a, b) {
var aa = getValue(a),
bb = getValue(b);
return isFinite(aa) && isFinite(bb) ? aa - bb : aa.localeCompare(bb);
});
}
var data = [[{ type: 'A', value: 10 }, { type: 'B', value: 20 }, { type: 'C', value: 15 }, { type: 'D', value: 'b' }], [{ type: 'A', value: 5 }, { type: 'B', value: 10 }, { type: 'C', value: 35 }, { type: 'D', value: 'a' }], [{ type: 'A', value: 30 }, { type: 'B', value: 30 }, { type: 'C', value: 25 }, { type: 'D', value: 'd' }], [{ type: 'A', value: 20 }, { type: 'B', value: 50 }, { type: 'C', value: 55 }, { type: 'D', value: 'c' }]];
sort(data, 'A');
console.log(data);
sort(data, 'D');
console.log(data);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can dynamically create a relationship for sorting using closures.
function sortBy(key){
return function(a,b){
return a[key] - b[key];
}
}
var index = {'A':0, 'B': 1, … };
var userPickedType = 'A';
yourarray.sort(sortBy(index[userPickedType]));

Categories

Resources