Related
Suppose I have an object:
let array = [
{a: 1, b: 5, c: 9},
{a: 2, b: 6, c: 10},
{a: 3, b: 7, c: 11},
{a: 4, b: 8, c: 12}
];
then I have a dictionary:
const columns = [
{ key: 'a', value: 'a' },
{ key: 'b', value: 'b' },
]
I want to filter out properties that are not defined in columns.
I have tried
array.map((x) => ({"a": x.a, "b": x.b}))
Is there a way to use the data defined in columns instead of manually typing all the properties?
Desired output:
[
{
"a": 1,
"b": 5
},
{
"a": 2,
"b": 6
},
{
"a": 3,
"b": 7
},
{
"a": 4,
"b": 8
}
]
You could map entries and get the new objects.
let
array = [{ a: 1, b: 5, c: 9 }, { a: 2, b: 6, c: 10 }, { a: 3, b: 7, c: 11 }, { a: 4, b: 8, c: 12 }],
columns = [{ key: 'a', value: 'a' }, { key: 'b', value: 'b' }],
keys = columns.map(({ key }) => key),
result = array.map(o => Object.fromEntries(keys.map(k => [k, o[k]])));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You could use this.
This uses just an array to hold the desired columns because I don't get why you would use a dictionary with key and value being the same.
let array = [
{ a: 1, b: 5, c: 9 },
{ a: 2, b: 6, c: 10 },
{ a: 3, b: 7, c: 11 },
{ a: 4, b: 8, c: 12 },
];
const desiredColumns = ["a", "b"];
const transformed = array.map(item => {
const obj = {};
desiredColumns.forEach(col => {
if(col in item){
obj[col] = item[col];
}
})
return obj;
})
console.log(array);
console.log(transformed)
Another, slightly less direct way using map() and reduce():
Create an array with all the keys we'll keep
Reduce the array to get the desired result
Add current key + value if key keep array
const array = [{a: 1, b: 5, c: 9}, {a: 2, b: 6, c: 10}, {a: 3, b: 7, c: 11}, {a: 4, b: 8, c: 12} ];
const columns = [{ key: 'a', value: 'a' }, { key: 'b', value: 'b' }, ];
const toKeep = columns.map(({ key }) => key).flat();
const result = array.map(a =>
Object.keys(a)
.reduce((prev, cur) => (toKeep.includes(cur)) ? { ...prev, [cur]: a[cur] } : prev, {})
);
console.log(result);
Result:
[
{
"a": 1,
"b": 5
},
{
"a": 2,
"b": 6
},
{
"a": 3,
"b": 7
},
{
"a": 4,
"b": 8
}
]
I am trying to add the key to each so that I can be able to easy make a multi scatter plot in d3. . I am not sure how to do it.
EDIT: TO CLARIFY what I meant.
Data:
var dataOriginal = {
Apples: [{"A":4,"B":null,"C":null,"D":2}, {"A":5,"B":null,"C":3,"D":2}],
Oranges: [{"A":3,"B":1,"C":4,"D":4.3}],
Jackfruit: [{"A":5,"B":4,"C":4,"D":3}],
Avocado: [{"A":null,"B":33,"C":2,"D":9.66}],
Pomegranate: [{"A":5,"B":3.5,"C":null,"D":6}]
}
Function:
const data = Object.keys(dataOriginal).map((key) => {
const temp = {...dataOriginal[key]};
temp.key = key;
return temp;
});
Results:
0:
0: {A: 4, B: null, C: null, D: 2}
1: {A: 5, B: null, C: 3, D: 2}
key: "Apples"
__proto__: Object
1:
0: {A: 3, B: 1, C: 4, D: 4.3}
key: "Oranges"
__proto__: Object
2:
0: {A: 5, B: 4, C: 4, D: 3}
key: "Jackfruit"
__proto__: Object
3:
0: {A: null, B: 33, C: 2, D: 9.66}
key: "Avocado"
__proto__: Object
4: {0: {…}, key: "Pomegranate"}
Desired results
: {A: 4, B: null, C: null, D: 2, key: "Apples"}
1: {A: 3, B: 1, C: 4, D: 4.3, key: "Oranges"}
2: {A: 5, B: 4, C: 4, D: 3, key: "Jackfruit"}
3: {A: null, B: 33, C: 2, D: 9.66, key: "Avocado"}
4: {A: 5, B: 3.5, C: null, D: 6, key: "Pomegranate"}
5: {A:5,B:null,C:3,D:2, key: "Apples"}
You need to reduce the object to get a single object with added values.
const
addByKey = array => array.reduce((a, b) => {
Object.entries(b).forEach(([k, v]) => a[k] = (a[k] || 0) + v);
return a;
}, {}),
dataOriginal = { Apples: [{ A: 4, B: null, C: null, D: 2 }, { A: 5, B: null, C: 3, D: 2 }], Oranges: [{ A: 3, B: 1, C: 4, D: 4.3 }], Jackfruit: [{ A: 5, B: 4, C: 4, D: 3 }], Avocado: [{ A: null, B: 33, C: 2, D: 9.66 }], Pomegranate: [{ A: 5, B: 3.5, C: null, D: 6 }] }
data = Object.keys(dataOriginal).map((key) => ({ ...addByKey(dataOriginal[key]), key }));
console.log(data);
.as-console-wrapper { max-height: 100% !important; top: 0; }
For getting single object with same keys, you could map the objects, add the key and get a flat array.
const
dataOriginal = { Apples: [{ A: 4, B: null, C: null, D: 2 }, { A: 5, B: null, C: 3, D: 2 }], Oranges: [{ A: 3, B: 1, C: 4, D: 4.3 }], Jackfruit: [{ A: 5, B: 4, C: 4, D: 3 }], Avocado: [{ A: null, B: 33, C: 2, D: 9.66 }], Pomegranate: [{ A: 5, B: 3.5, C: null, D: 6 }] }
data = Object
.keys(dataOriginal)
.flatMap(key => dataOriginal[key].map(o => ({ ...o, key })));
console.log(data);
.as-console-wrapper { max-height: 100% !important; top: 0; }
The reason why {"A":5,"B":null,"C":3,"D":2} is missed is because, index 0 is hardcoded in the code.
const temp = {...dataOriginal[key][0]};
Alternate solution:
var dataOriginal = {
Apples: [{"A":4,"B":null,"C":null,"D":2}, {"A":5,"B":null,"C":3,"D":2}],
Oranges: [{"A":3,"B":1,"C":4,"D":4.3}],
Jackfruit: [{"A":5,"B":4,"C":4,"D":3}],
Avocado: [{"A":null,"B":33,"C":2,"D":9.66}],
Pomegranate: [{"A":5,"B":3.5,"C":null,"D":6}]
}
const myData =[]
Object.keys(dataOriginal).map((key) => {
for (let i = 0; i < dataOriginal[key].length; i++) {
myData.push({...dataOriginal[key][i], key})
}
})
console.log(myData)
Suppose I have a input array like the following
var inputArray = [
{a: 1, b: 1, c: 1, d: 1, value: 1, rank: 1},
{a: 1, b: 1, c: 1, d: 1, value: 2, rank: 2},
{a: 1, b: 1, c: 1, d: 1, value: 3, rank: 3},
{a: 1, b: 1, c: 1, d: 1, value: 4, rank: 4},
{a: 1, b: 1, c: 1, d: 1, value: 5, rank: 5},
{a: 1, b: 2, c: 1, d: 1, value: 1, rank: 1},
{a: 1, b: 2, c: 1, d: 1, value: 2, rank: 2},
{a: 1, b: 2, c: 1, d: 1, value: 3, rank: 3},
{a: 1, b: 2, c: 1, d: 1, value: 4, rank: 4},
{a: 1, b: 2, c: 1, d: 1, value: 5, rank: 5}
]
I want to transform my inputArray to the following outputArray
var outputArray = [
{
a: 1,
b: 1,
c: 1,
d: 1,
values:{
"1":{value: 1},
"2":{value: 2},
"3":{value: 3},
"4":{value: 4},
"5":{value: 5}
}
},
{
a: 1,
b: 2,
c: 1,
d: 1,
values:{
"1":{value: 1},
"2":{value: 2},
"3":{value: 3},
"4":{value: 4},
"5":{value: 5}
}
}
]
That means, I need create a dictionary for same property of a, b, c and d where the value of property rank is the key of the dictionary and the value of the dictionary is an object where the only property is value.
We assume that the inputArray will not be sorted with respect to the combination of a, b, c and d. So, my approach is like this,
(function(){
var inputArray = [
{a: 1, b: 1, c: 1, d: 1, value: 1, rank: 1},
{a: 1, b: 1, c: 1, d: 1, value: 2, rank: 2},
{a: 1, b: 1, c: 1, d: 1, value: 3, rank: 3},
{a: 1, b: 1, c: 1, d: 1, value: 4, rank: 4},
{a: 1, b: 1, c: 1, d: 1, value: 5, rank: 5},
{a: 1, b: 2, c: 1, d: 1, value: 1, rank: 1},
{a: 1, b: 2, c: 1, d: 1, value: 2, rank: 2},
{a: 1, b: 2, c: 1, d: 1, value: 3, rank: 3},
{a: 1, b: 2, c: 1, d: 1, value: 4, rank: 4},
{a: 1, b: 2, c: 1, d: 1, value: 5, rank: 5}
]
var temp = inputArray.sort(function(valA, valB){
if(valA.a === valB.a){
if(valA.b === valB.b){
if(valA.c === valB.c){
return valA.d < valB.d;
}
return valA.c < valB.c;
}
return valA.b < valB.b;
}
return valA.a < valB.a;
});
var outputArray = [],
currentIndex = 0;
for(var i = 0; i < inputArray.length; i++){
if(i > 0 && isConfigurationSame(inputArray[i], inputArray[i-1])){
outputArray[currentIndex-1].values[inputArray[i].rank] = {
value: inputArray[i].value
}
}
else{
outputArray.push(mapToOutputArrayObject(inputArray[i]));
currentIndex++;
}
}
console.log(outputArray);
function isConfigurationSame(A, B) {
return A.a === B.a
&& A.b === B.b
&& A.c === B.c
&& A.d === B.d;
}
function mapToOutputArrayObject(val){
var row = {};
row['a'] = val.a;
row['b'] = val.b;
row['c'] = val.c;
row['d'] = val.d;
row['values'] = {};
row.values[val.rank] = {
value: val.value
}
return row;
}
}());
But the problem is, this thing is really getting more time if the length of input array is huge. This multi-criteria sorting also taking much time.
Is there any better approach to accomplish the result more efficiently with less time?
Thanks for your time and patience.
UPDATE: The values of a, b, c and d can be integer or null.
You could create a hashtable and generate a unique key based on a, b, c and d:
const hash = {};
for(const { a, b, c, d, value, rank } of array) {
const key = JSON.stringify([a, b, c, d]); // generate a unique, but not random key
if(hash[key]) { // check if it already exists,
hash[key].values[rank] = value; // merge
} else {
hash[key] = { // create a new entry
a, b, c, d,
values: { [rank]: value },
};
}
}
const result = Object.values(hash); // turn the object into an array
That is O(n), which is better as the time complexity of any .sort implementation (but it only works if a, b, c and d are serializable (like in this case)).
You could take a Map and an array of the grouping keys and collect the values for each group.
var array = [{ a: 1, b: 1, c: 1, d: 1, value: 1, rank: 1 }, { a: 1, b: 1, c: 1, d: 1, value: 2, rank: 2 }, { a: 1, b: 1, c: 1, d: 1, value: 3, rank: 3 }, { a: 1, b: 1, c: 1, d: 1, value: 4, rank: 4 }, { a: 1, b: 1, c: 1, d: 1, value: 5, rank: 5 }, { a: 1, b: 2, c: 1, d: 1, value: 1, rank: 1 }, { a: 1, b: 2, c: 1, d: 1, value: 2, rank: 2 }, { a: 1, b: 2, c: 1, d: 1, value: 3, rank: 3 }, { a: 1, b: 2, c: 1, d: 1, value: 4, rank: 4 }, { a: 1, b: 2, c: 1, d: 1, value: 5, rank: 5 }],
keys = ['a', 'b', 'c', 'd'],
result = [],
map = new Map;
array.forEach(o => {
var key = keys.map(k => o[k]).join('|'),
temp = map.get(key);
if (!temp) {
map.set(key, temp = Object.assign(...keys.map(k => ({ [k]: o[k] })), { values: {} }));
result.push(temp);
}
temp.values[o.rank] = { value: o.value };
});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Here is a stab at it using Set, Map and a const method to build the Values object.
var inputArray = [
{a: 1, b: 1, c: 1, d: 1, value: 1, rank: 1},
{a: 1, b: 1, c: 1, d: 1, value: 2, rank: 2},
{a: 1, b: 1, c: 1, d: 1, value: 3, rank: 3},
{a: 1, b: 1, c: 1, d: 1, value: 4, rank: 4},
{a: 1, b: 1, c: 1, d: 1, value: 5, rank: 5},
{a: 1, b: 2, c: 1, d: 1, value: 1, rank: 1},
{a: 1, b: 2, c: 1, d: 1, value: 2, rank: 2},
{a: 1, b: 2, c: 1, d: 1, value: 3, rank: 3},
{a: 1, b: 2, c: 1, d: 1, value: 4, rank: 4},
{a: 1, b: 2, c: 1, d: 1, value: 5, rank: 5}
];
const getValueObject = (a,b,c,d, arr) => {
let obj = {};
arr.filter(i => i.a === a &&
i.b === b &&
i.c ===c &&
i.d === d)
.forEach(item => obj[item.value] = item.rank);
return obj;
};
// Get a set based on the key a,b,c,d
let newArray = [...new Set(inputArray.map(({a,b,c,d}) => `${a},${b},${c},${d}`))]
.map(item => {
let [a,b,c,d] = item.split(',').map(i => parseInt(i));
// filter and add
return {
a: a,
b: b,
c: c,
d: d,
values: getValueObject(a,b,c,d, inputArray)
};
});
console.log(newArray);
Here is another option, first grouping by a, b, c and d. Then mapping over each group transforming the value and rank.
var inputArray = [{a: 1, b: 1, c: 1, d: 1, value: 1, rank: 1}, {a: 1, b: 1, c: 1, d: 1, value: 2, rank: 2}, {a: 1, b: 1, c: 1, d: 1, value: 3, rank: 3}, {a: 1, b: 1, c: 1, d: 1, value: 4, rank: 4}, {a: 1, b: 1, c: 1, d: 1, value: 5, rank: 5}, {a: 1, b: 2, c: 1, d: 1, value: 1, rank: 1}, {a: 1, b: 2, c: 1, d: 1, value: 2, rank: 2}, {a: 1, b: 2, c: 1, d: 1, value: 3, rank: 3}, {a: 1, b: 2, c: 1, d: 1, value: 4, rank: 4}, {a: 1, b: 2, c: 1, d: 1, value: 5, rank: 5}];
function groupBy(array, callback) {
return array.reduce((groups, item, ...args) => {
const key = callback(item, ...args),
group = groups[key] || (groups[key] = []);
group.push(item);
return groups;
}, {});
};
console.log(
Object
.values( groupBy(inputArray, ({a, b, c, d}) => [a, b, c, d]) )
.map(group => {
const {a, b, c, d} = group[0],
values = {};
group.forEach(({value, rank}) => values[rank] = {value});
return {a, b, c, d, values};
})
);
I have a big array with data. Here is a example of the structure:
let data = [
{
date: '2018-11-22',
values: {
a: 10,
b: 20,
c: 5,
},
},
{
date: '2018-11-17',
values: {
a: 5,
b: 10,
c: 15,
},
},
{
date: '2018-06-29',
values: {
a: 10,
b: 30,
c: 10,
},
},
{
date: '2017-12-20',
values: {
a: 30,
b: 40,
c: 5,
},
},
];
I need this data structured in a new array by month and year. The value attributes should be summed up for each month.
So the new array for the example should look like this:
let sortedData = [
{
date: '2018-11',
values: {
a: 15,
b: 30,
c: 20,
},
},
{
date: '2018-06',
values: {
a: 10,
b: 30,
c: 10,
},
},
{
date: '2017-12',
values: {
a: 30,
b: 40,
c: 5,
},
},
];
I'm trying for hours to write a working function but I can't handle it.
Any ideas how I can bundle an array like this?
Thanks for your help!
You can use Array.reduce for this
let data = [ { date: '2018-11-22', values: { a: 10, b: 20, c: 5, }, }, { date: '2018-11-17', values: { a: 5, b: 10, c: 15, }, }, { date: '2018-06-29', values: { a: 10, b: 30, c: 10, }, }, { date: '2017-12-20', values: { a: 30, b: 40, c: 5, }, },];
let res = data.reduce((o, {date, values}) => {
let k = date.slice(0, 7)
o[k] = o[k] || {date: k, values: {a: 0, b: 0, c:0}}
o[k].values.a += values.a
o[k].values.b += values.b
o[k].values.c += values.c
return o
}, {})
console.log(Object.values(res))
You can also do make it more concise and not deal with the individual values props like this:
let data = [{ date: '2018-11-22', values: { a: 10, b: 20, c: 5, }, }, { date: '2018-11-17', values: { a: 5, b: 10, c: 15, }, }, { date: '2018-06-29', values: { a: 10, b: 30, c: 10, }, }, { date: '2017-12-20', values: { a: 30, b: 40, c: 5, }, }, ];
const result = data.reduce((r, {date, values}) => {
date = date.substr(0,7)
r[date] = r[date]
? (Object.keys(values).forEach(k => r[date].values[k] += values[k]), r[date])
: {date, values}
return r
}, {})
console.log(Object.values(result))
This way you would not care if there are 3 of 10 properties in values and you get more generic solution.
I am trying to transform a nested structure, using the library of lodash, I have achieved the expected result, but they are not functional if the structure changes, so I come to you to help me make more robust the function that transforms the JSON.
the initial structure looks like this
const data = {
foo: {
bar: {
baz: [{ a: 1, b: 2, c: 3 }]
},
baz: {
bar: [{ a: 1, b: 2, c: 3 }]
},
foo: {
bar: [{ a: 1, b: 2, c: 3 }]
}
},
bar: {
baz: {
bar: [{ a: 1, b: 2, c: 3 }]
}
},
baz: {
foo: {
bar: [{ a: 1, b: 2, c: 3 }]
}
}
};
after being transformed
const transform = [
{
name: 'barfoo',
results: [{ a: 1, b: 2, c: 3 }]
},
{
name: 'bazfoo',
results: [{ a: 1, b: 2, c: 3 }]
},
{
name: 'foofoo',
results: [{ a: 1, b: 2, c: 3 }]
},
{
name: 'bazbar',
results: [{ a: 1, b: 2, c: 3 }]
},
{
name: 'foobaz',
results: [{ a: 1, b: 2, c: 3 }]
}
];
The idea of the transformation is to join the nested key of the first level with the key of the parent node to generate the value of name in the new object and the value of the object in the 2 level as the value of results
for example for the first iteration of foo object in data
name = key(foo.bar) + key(foo)
results = value(foo.bar.baz)
name = 'barfoo'
results = [{ a: 1, b: 2, c: 3 }]
name = key(foo.baz) + key(foo)
results = value(foo.baz.bar)
name = 'bazfoo'
results = [{ a: 1, b: 2, c: 3 }]
name = key(foo.foo) + key(foo)
results = value(foo.foo.bar)
name = 'foofoo'
results = [{ a: 1, b: 2, c: 3 }]
and so with the other objects that are inside data.
I'm not sure if the structure will ever vary, but I added a few extra test cases so you can see how this will behave in some additional scenarios.
const data = {
foo: {
bar: {
baz: [{ a: 1, b: 2, c: 3 }]
},
baz: {
bar: [{ a: 1, b: 2, c: 3 }]
},
foo: {
bar: [{ a: 1, b: 2, c: 3 }]
}
},
bar: {
baz: {
bar: [{ a: 1, b: 2, c: 3 }]
}
},
baz: {
foo: {
bar: [{ a: 1, b: 2, c: 3 }]
}
},
a1: {
a2: [{ a: 1, b: 2, c: 3 }]
},
b1: [{ a: 1, b: 2, c: 3 }],
c1: {
c2: {
c3: {
c4: [{ a: 1, b: 2, c: 3 }]
}
},
c5: [{ a: 1, b: 2, c: 3 }]
},
d1: {
d2: {
d3: undefined
}
},
e1: {
e2: {
e3: null
}
},
f1: {
f2: {
// Ignored
}
}
};
function transformObject(object, name) {
if (!name) {
name = "";
}
return _.flatten(_.map(object, function(value, key) {
if (typeof value === "undefined"
|| value === null
|| _.isArray(value)) {
return {
name: name,
results: value
}
}
var objectName = key + name;
return transformObject(value, objectName);
}));
}
transformObject(data);