Related
I have an array of objects.
I want to merge the objects into a single array by same key. At that time, I also want to include other value in the array together.
It doesn't matter whether the merged array is an array or an object.
Current array:
[
{
"datetime": "2022-01-10",
"a": 0.5,
"b": 80.6,
"c": 1002.2
},
{
"datetime": "2022-01-11",
"a": 0.7,
"b": 80.4,
"c": 1002.4
},
{
"datetime": "2022-01-12",
"a": 0.4,
"b": 80.2,
"c": 1002.3
}
]
Expected result:
[
[
["2022-01-10", 0.5], ["2022-01-11", 0.7], ["2022-01-12", 0.4]
],
[
["2022-01-10", 80.6], ["2022-01-11", 80.4], ["2022-01-12", 1002.4]
],
[
["2022-01-10", 1002.2], ["2022-01-11", 1002.4], ["2022-01-12", 1002.3]
]
]
or
{
"a": [
["2022-01-10", 0.5], ["2022-01-11", 0.7], ["2022-01-12", 0.4]
],
"b": [
["2022-01-10", 80.6], ["2022-01-11", 80.4], ["2022-01-12", 1002.4]
],
"c": [
["2022-01-10", 1002.2], ["2022-01-11", 1002.4], ["2022-01-12", 1002.3]
]
}
I use forEach() and it works.
But I want to know if there are other ways.
const foo = [[], [], []];
json.forEach((item) => {
const [a, b, c] = foo;
a.push([item.datetime, item.a]);
b.push([item.datetime, item.b]);
c.push([item.datetime, item.c]);
});
As for generic solutions (only the datetime property of each item needs to be known, thus the solutions are agnostic to all other properties) to both of the OP's use cases,
the array of arrays where the nested array items are tuples of distinct datetime values and values of same item keys
and the object of arrays where the arrays are the same but get referred to by keys which are distinct from datetime,
one easily could use the same reduce based approach only that
for the latter result (object based key specific array of arrays) one passes an object as initial value and creates and aggregates the key specific nested arrays upon the currently processed key of each item's rest-property data-entries,
whereas for the former result (array of arrays) the initial value is an array where one needs to create and/or access each key specific inner array by the current index of each of the rest-data's values.
const sampleData = [{
datetime: "2022-01-10",
a: 0.5,
b: 80.6,
c: 1002.2,
}, {
datetime: "2022-01-11",
a: 0.7,
b: 80.4,
c: 1002.4,
}, {
datetime: "2022-01-12",
a: 0.4,
b: 80.2,
c: 1002.3,
}];
console.log(
sampleData
.reduce((result, { datetime, ...rest }) => {
Object
.values(rest)
.forEach((value, idx) =>
(result[idx] ??= []).push([datetime, value])
);
return result;
}, [])
);
console.log(
sampleData
.reduce((result, { datetime, ...rest }) => {
Object
.entries(rest)
.forEach(([key, value]) =>
(result[key] ??= []).push([datetime, value])
);
return result;
}, {})
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
In case the key (insertion) order of the original array items can not be guarantied amongst all items, one should use the key-specific second solution, where one would pass the created object to Object.values in order to get an array of arrays ...
const sampleData = [{
datetime: "2022-01-10",
a: 0.5,
b: 80.6,
c: 1002.2,
}, {
datetime: "2022-01-11",
a: 0.7,
b: 80.4,
c: 1002.4,
}, {
datetime: "2022-01-12",
a: 0.4,
b: 80.2,
c: 1002.3,
}];
console.log(
Object
.values(
sampleData
.reduce((result, { datetime, ...rest }) => {
Object
.entries(rest)
.forEach(([key, value]) =>
(result[key] ??= []).push([datetime, value])
);
return result;
}, {})
)
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
You can use reduce function where the initial value will be an empty object.
after that, you can check if that object contains that particular key or not and push the data accordingly
a = [
{
datetime: "2022-01-10",
a: 0.5,
b: 80.6,
c: 1002.2,
},
{
datetime: "2022-01-11",
a: 0.7,
b: 80.4,
c: 1002.4,
},
{
datetime: "2022-01-12",
a: 0.4,
b: 80.2,
c: 1002.3,
},
];
const solution = (key) => {
return a.reduce((acc, { [key]: keyValue, ...next }) => {
Object.entries(next).forEach(([dataKey, dataValue]) => {
(acc[dataKey] ??= []).push([keyValue, dataValue]);
});
return acc;
}, {});
};
console.log('Solution 1: ',Object.values(solution("datetime")));
console.log('Solution 2: ',solution("datetime"));
you can use javascript reduce to optimize this:
const data = temp.reduce((acc,{a,b,c,datetime})=>{
const [first,second,third] = acc;
first.push([datetime, a]);
second.push([datetime, b]);
third.push([datetime, c]);
return acc;
},[[],[],[]])
const Json = [
{
"datetime": "2022-01-10",
"a": 0.5,
"b": 80.6,
"c": 1002.2
},
{
"datetime": "2022-01-11",
"a": 0.7,
"b": 80.4,
"c": 1002.4
},
{
"datetime": "2022-01-12",
"a": 0.4,
"b": 80.2,
"c": 1002.3
}
]
const func = (arr, i) => {
let result = [];
const constants = ["a", "b", "c"];
for (let index = 0; index < arr.length; index++) {
result.push([arr[index].datetime, arr[index][`${constants[i]}`]]);
}
return result;
}
const result = Json.map((d, i) => {
return func(Json, i);
});
console.log(result)
See this it uses map and then returns an array just like your ist
const data = [{
datetime: "2022-01-10",
a: 0.5,
b: 80.6,
c: 1002.2,
}, {
datetime: "2022-01-11",
a: 0.7,
b: 80.4,
c: 1002.4,
}, {
datetime: "2022-01-12",
a: 0.4,
b: 80.2,
c: 1002.3,
}];
const res = data.map((m ,_) => [
[m.datetime,m.a],
[m.datetime,m.b],
[m.datetime,m.c],
]);
console.log({ res });
Or 2nd method because of comments
const data = [{
datetime: "2022-01-10",
a: 0.5,
b: 80.6,
c: 1002.2,
}, {
datetime: "2022-01-11",
a: 0.7,
b: 80.4,
c: 1002.4,
}, {
datetime: "2022-01-12",
a: 0.4,
b: 80.2,
c: 1002.3,
}];
const res = ["a","b","c"].map(key => data.map(obj => [
obj.datetime,
obj[key],
]));
console.log({ res });
I have an object like this
const obj = {
name: "abc",
arr: [{
key1: "value1",
arr1: [1, 2, 3]
}, {
key1: "value2",
arr1: [4, 5, 6]
}]
}
Here, i want to add the lengths of arrays arr1 and arr2, and return 6 as an answer. I know looping and calculating lenghts by for-in is one solution, but what can be a faster and more precise solution?
this is the simplest implementation I can think of using reduce
const obj = {
name: "abc",
arr: [{
key1: "value1",
arr1: [1, 2, 3]
}, {
key1: "value2",
arr1: [4, 5, 6]
}]
}
const result = obj.arr.reduce((res, {arr1}) => res + arr1.length, 0)
console.log(result)
It looks you want to count the lengths of arrays that only have primitive values as members, not of arrays which have (some) objects/arrays as members.
Here is a proposed recursive solution:
const isPrimitive = val => Object(val) !== val;
const isArrayOfPrimitive = arr => arr?.every?.(isPrimitive);
const deepLength = obj => isPrimitive(obj) ? 0
: Object.values(obj).reduce((sum, val) => sum + deepLength(val),
isArrayOfPrimitive(obj) ? obj.length : 0);
// Example run:
const obj = {
name: "abc",
arr: [{
key1: "value1",
arr1: [1, 2, 3]
}, {
key2: "value2",
arr2: [4, 5, 6]
}]
};
console.log(deepLength(obj)); // 6
const obj = {
name: "abc",
arr: [
{ key1: "value1", arr1: [1, 2, 3, 4, 6, 7, 9] },
{ key2: "value2", arr2: [4, 5, 6] }
]
}
function getNumbers(arr) {
let a = arr['arr'][0].arr1.length;
let b = arr['arr'][1].arr2.length;
let sum = a + b;
return sum;
}
console.log(getNumbers(obj));
I have an array that looks like this,
['event_tag', 'workflow_tag', 'created_timestamp', 'success']
and an array of objects where the object looks like,
{
"created_timestamp": "2022-04-01T13:14:53.028002Z",
"workflow_tag": "dj807",
"event_tag": "refresh",
"success": true
}
What I am wanting to do is make the above object and any other objects in that array match the order of the values in the first array so the finished object should look like,
{
"event_tag": "refresh",
"workflow_tag": "dj807",
"created_timestamp": "2022-04-01T13:14:53.028002Z",
"success": true
}
I have tried the following so far,
const keys = ['event_tag', 'workflow_tag', 'created_timestamp', 'success'];
newRowData = parsedRows.reduce((obj, v) => {
obj[v] = keys[v];
return obj
}, {});
But this returns,
{[object Object]: undefined}
You could order the keys by constructing a new object inside of an Array#map:
const parsedRows = [ { a: 1, c: 3, d: 4, b: 2, }, { b: 6, a: 5, c: 7, d: 8, }, { d: 12, b: 10, a: 9, c: 11, }, ];
const order = ['a', 'b', 'c', 'd'];
let newData = parsedRows.map(row => {
let newRow = {};
for (let key of order) {
newRow[key] = row[key];
}
return newRow;
});
console.log(newData);
Instead of iterating over Rows, Iterate on keys either map/reduce.
const keys = ["event_tag", "workflow_tag", "created_timestamp", "success"];
const obj = {
created_timestamp: "2022-04-01T13:14:53.028002Z",
workflow_tag: "dj807",
event_tag: "refresh",
success: true,
};
const res = Object.assign({}, ...keys.map((key) => ({ [key]: obj[key] })));
console.log(res)
I have an array of objects like this:
const arrayOfObjects = [
{ A: 1, B: 2, C: 3 },
{ A: 3, B: 4, C: 1 }
]
And another array which is called "headers"
const headers = [
['A', 'B'],
['C']
]
I have to create an array similar to the first one but, with those objects splited by what headers have in it's arrays.
This should be the goal:
const result = [
[
{ A: 1, B: 2 },
{ C: 3 }
],
[
{ A: 3, B: 4 },
{ C: 1 }
]
]
I tried by doing a "base" array with:
const baseArray = []
headers.forEach((header) => {
const objFromHeader = {};
header.forEach((head) => {
objFromHeader[head] = 0;
});
baseArray.push(objFromHeader);
});
That will give me the result array but with 0 values for each key.
And then loop for the first array and put inside another array the base array with the correct values.
Then I wanted to fill each key according to the value that comes from arrayOfObjects but here is where I can't see how could I loop that array of objects and put the correct value. The only problem with that approach is that the result array will have some 0 values that come from the initiation array that I'm using, it would be better to me to only put the objects that actually have values and not 0 (I was thinking on another function to delete those keys with value = 0...)
How could I achieve it in a better way?
Fiddle:
https://jsfiddle.net/pmiranda/Lpscz6vt/
When iterating over an object, use findIndex on the headers to identify which index in the headers array the property being iterated over should go into. Create an object there if it doesn't exist yet, and set the property.
const arrayOfObjects = [
{ A: 1, B: 2, C:3 },
{ A: 3, B: 4, C:1 }
];
const headers = [
['A', 'B'],
['C']
];
const toResultItem = (object) => {
const resultItem = [];
for (const [key, value] of Object.entries(object)) {
const headersIndex = headers.findIndex(arr => arr.includes(key));
resultItem[headersIndex] ??= {};
resultItem[headersIndex][key] = value;
}
return resultItem;
};
console.log(arrayOfObjects.map(toResultItem));
const arrayOfObjects = [
{ A: 1, B: 2, C: 3 },
{ A: 3, B: 4, C: 1 },
];
const headers = [['A', 'B'], ['C', 'D']];
const result = arrayOfObjects.map((obj) =>
headers.map((header) =>
header.reduce((acc, key) => {
acc[key] = obj[key];
return Object.keys(acc).reduce((newAcc, key) => {
if (acc[key]) {
newAcc[key] = acc[key];
}
return newAcc;
}
, {});
}, {})
)
);
console.log(result);
Array.forEach implementation
Logic
Loop through arrayOfObjects array.
Inside that, loop through headers array.
Inside that, loop through each array in the headers array.
Create an empty object and assign the property from nodes in headers array with values from objects in arrayOfObjects array.
const arrayOfObjects = [
{ A: 1, B: 2, C: 3 },
{ A: 3, B: 4, C: 1 }
];
const headers = [
['A', 'B'],
['C']
];
const baseArray = []
arrayOfObjects.forEach((obj) => {
const childNode = [];
headers.forEach((head) => {
const node = {};
head.forEach((key) => node[key] = obj[key]);
childNode.push(node);
});
baseArray.push(childNode);
});
console.log(baseArray)
Array.map and Array.reduce implementation.
Using the same logic implementes in the above solution, we can rewrite this using Array.map and Array.reduce as below.
const arrayOfObjects = [
{ A: 1, B: 2, C: 3 },
{ A: 3, B: 4, C: 1 }
];
const headers = [
['A', 'B'],
['C']
];
const output = arrayOfObjects.map((obj) => {
return headers.map((header) => {
return header.reduce((acc, curr) => {
acc[curr] = obj[curr];
return acc;
}, {});
})
})
console.log(output);
interface FormValues {
key: string;
value: any;
}
const array: FormValues[] = [
{
key: 'A',
value: 1 // number
},
{
key: 'A',
value: 1 // number
},
{
key: 'A',
value: 'str' // string
},
{
key: 'C',
value: { a: 1, b: '2' } // object
},
{
key: 'C',
value: ['a','2'] // array
},
{
key: 'C',
value: ['a','2'] // array
}
{
key: 'B',
value: true // boolean
}
]
I want to filter the objects based on field value, which can have a value of any type.
I tried to do it like this; my solution is not working for nested object checks.
const key = 'value';
const arrayUniqueByKey = [...new Map(array.map(item => [item[key], item])).values()];
output :
[{
key: 'A',
value: 1 // number
},
{
key: 'A',
value: 'str' // string
},
{
key: 'C',
value: { a: 1, b: '2' } // object
},
{
key: 'C',
value: ['a','2'] // array
},
{
key: 'B',
value: true // boolean
}]
You need to decide what makes two distinct objects "equal". In JavaScript, all built-in comparisons of objects (which includes arrays) are by reference. That means ['a','2'] === ['a','2'] is false because two distinct array objects exist, despite having the same contents. See How to determine equality for two JavaScript objects? for more information.
I will take the approach that you would like two values to be considered equal if they serialize to the same value via a modified version of JSON.stringify() where the order of property keys are guaranteed to be the same (so {a: 1, b: 2} and {b: 2, a: 1} will be equal no matter how those are stringified). I use a version from this answer to do so:
function JSONstringifyOrder(obj: any, space?: number) {
var allKeys: string[] = [];
var seen: Record<string, null | undefined> = {};
JSON.stringify(obj, function (key, value) {
if (!(key in seen)) {
allKeys.push(key); seen[key] = null;
}
return value;
});
allKeys.sort();
return JSON.stringify(obj, allKeys, space);
}
And now I can use that to make the keys of your Map:
const arrayUniqueByKey = [...new Map(array.map(
item => [JSONstringifyOrder(item[key]), item]
)).values()];
And you can verify that it behaves as you'd like:
console.log(arrayUniqueByKey);
/* [{
"key": "A",
"value": 1
}, {
"key": "A",
"value": "str"
}, {
"key": "C",
"value": {
"a": 1,
"b": "2"
}
}, {
"key": "C",
"value": [
"a",
"2"
]
}, {
"key": "B",
"value": true
}] */
Playground link to code
This will combine any duplicate keys, creating a new property values to hold the array of combined values (from like keys).
const array = [{key: 'A', value: 1},{key: 'A', value: 'str'},{key: 'C', value: { a: 1, b: '2'}},{key: 'B',value: true}]
const arrayUniqueByKey = [array.reduce((b, a) => {
let f = b.findIndex(c => c.key === a.key)
if (f === -1) return [...b, a];
else {
b[f].values = [...[b[f].value], a.value];
return b
}
}, [])];
console.log(arrayUniqueByKey)
You can use Array.prototype.reduce() combined with JSON.stringify() and finaly get the result array of values with Object.values()
const array = [{key: 'A',value: 1,},{key: 'A',value: 1,},{key: 'A',value: 'str',},{key: 'C',value: { a: 1, b: '2' },},{key: 'C',value: ['a', '2'],},{key: 'C',value: ['a', '2'],},{key: 'B',value: true}]
const result = Object.values(array.reduce((a, c) => ((a[JSON.stringify(c)] = c), a), {}))
console.log(result)