Group data from objects in an array - javascript

I have been struggling for a few hours now and can't get it right. I need a function that groups objects and counts or sums some items in the object. My var vData is the starting point that has to be transformed to my var result as in the example below.
var vData = [{group: 1, subgroup: 1, position: 1, level: 1, action: 1, desciption: 'description #1'},
{group: 1, subgroup: 1, position: 1, level: 2, action: 1, desciption: 'description #2'},
{group: 1, subgroup: 1, position: 2, level: 3, action: 1, desciption: 'description #3'},
{group: 1, subgroup: 1, position: 4, level: 3, action: 1, desciption: 'description #4'},
{group: 1, subgroup: 2, position: 2, level: 3, action: 1, desciption: 'description #5'},
{group: 1, subgroup: 2, position: 2, level: 1, action: 2, desciption: 'description #6'},
{group: 2, subgroup: 1, position: 1, level: 1, action: 1, desciption: 'description #7'},
{group: 2, subgroup: 2, position: 1, level: 3, action: 1, desciption: 'description #8'},
{group: 2, subgroup: 3, position: 1, level: 1, action: 1, desciption: 'description #9'}];
function fGroupData(vData){
/*
var result = [{group: 1, subgroup: 1, count_of_objects: 4, count_of_unique_positions: 3, sum_of_level: 9},
{group: 1, subgroup: 2, count_of_objects: 2, count_of_unique_positions: 1, sum_of_level: 4},
{group: 2, subgroup: 1, count_of_objects: 1, count_of_unique_positions: 1, sum_of_level: 1},
{group: 2, subgroup: 2, count_of_objects: 1, count_of_unique_positions: 1, sum_of_level: 3},
{group: 2, subgroup: 3, count_of_objects: 1, count_of_unique_positions: 1, sum_of_level: 1}];
*/
}

I suggest to split the task in two parts, first collect the levels and then value the levels.
function fGroupData(vData) {
var result = vData.reduce(function (r, a) {
var index;
r.some(function (b, i) {
if (a.group === b.group && a.subgroup === b.subgroup) {
index = i;
return true;
}
}) ? r[index].levels.push(a.level) : r.push({ group: a.group, subgroup: a.subgroup, levels: [a.level] });
return r;
}, []);
result.forEach(function (a) {
a.count_of_objects = a.levels.length;
a.count_of_unique_positions = 0;
a.levels.sort().reduce(function (last, el) {
last !== el && a.count_of_unique_positions++;
return el;
}, undefined);
a.sum_of_level = a.levels.reduce(function (a, b) { return a + b; });
delete a.levels;
});
return result;
}
var vData = [
{ group: 1, subgroup: 1, position: 1, level: 1, action: 1, desciption: 'description #1' },
{ group: 1, subgroup: 1, position: 1, level: 2, action: 1, desciption: 'description #2' },
{ group: 1, subgroup: 1, position: 2, level: 3, action: 1, desciption: 'description #3' },
{ group: 1, subgroup: 1, position: 4, level: 3, action: 1, desciption: 'description #4' },
{ group: 1, subgroup: 2, position: 2, level: 3, action: 1, desciption: 'description #5' },
{ group: 1, subgroup: 2, position: 2, level: 1, action: 2, desciption: 'description #6' },
{ group: 2, subgroup: 1, position: 1, level: 1, action: 1, desciption: 'description #7' },
{ group: 2, subgroup: 2, position: 1, level: 3, action: 1, desciption: 'description #8' },
{ group: 2, subgroup: 3, position: 1, level: 1, action: 1, desciption: 'description #9' }
];
document.write('<pre>' + JSON.stringify(fGroupData(vData), 0, 4) + '</pre>');

There must have other elegant ways. But the following function will do the output you want:
function getFormattedJSON(vData)
{
var tempArr = [];
var result = [];
var finalResult = [];
for(i in vData){
var row = vData[i];
var uKey = row.group + ':' + row.subgroup;
if(tempArr.indexOf(uKey) > -1)
{
objectCnt.push(uKey);
if(uniquePositions.indexOf(row.position) < 0) {
uniquePositions.push(row.position);
}
groupLevel.push(row.level);
result[uKey][0] = {group: row.group, subgroup: row.subgroup, count_of_objects: objectCnt.length, count_of_unique_positions: uniquePositions.length, sum_of_level: groupLevel.reduce((a, b) => a + b)};
}
else{
tempArr.push(uKey);
result[uKey] = [];
var objectCnt = [];
var uniquePositions = [];
var groupLevel = [];
objectCnt.push(uKey);
uniquePositions.push(row.position);
groupLevel.push(row.level);
result[uKey].push({group: row.group, subgroup: row.subgroup, count_of_objects: 1, count_of_unique_positions: 1, sum_of_level: row.level});
}
}
for(j in result){
finalResult.push(result[j][0]);
}
return finalResult;
}
console.log(getFormattedJSON(vData));

Related

How change a value in an array of objects with the value of the array index?

I use react-beautiful-dnd. My first data is stored in a array of objects "characters" :
const characters =
[
{id: 1, position: 0, type: 1, idLink: 2},
{id: 2, position: 1, type: 1, idLink: 3},
{id: 3, position: 2, type: 2, idLink: 1},
{id: 4, position: 3, type: 1, idLink: 1},
]
When I move an element of my list, I send the result in a function to update the index in the array "characters".
function handleOnDragEnd(result) {
if (!result.destination) return;
const items = Array.from(characters);
const [reorderedItem] = items.splice(result.source.index, 1);
items.splice(result.destination.index, 0, reorderedItem);
updateCharacters(items);
}
Works fine my first element with "id:1" is now in the last postion :
const characters =
[
{id: 2, position: 1, type: 1, idLink: 3},
{id: 3, position: 2, type: 2, idLink: 1},
{id: 4, position: 3, type: 1, idLink: 1},
{id: 1, position: 0, type: 1, idLink: 2},
]
I try now many times to insert a new value for all "position". I need to change the value of "position" from its object's index value.
function handleOnDragEnd(result) {
if (!result.destination) return;
const items = Array.from(characters);
const [reorderedItem] = items.splice(result.source.index, 1);
items.splice(result.destination.index, 0, reorderedItem);
for (let i = 0; i < items.length; i++) {
items.splice(i, 1, result.destination.index);
}
updateCharacters(items);
}
It doesn't work well. I need to have this :
const characters =
[
{id: 2, position: 0, type: 1, idLink: 3},
{id: 3, position: 1, type: 2, idLink: 1},
{id: 4, position: 2, type: 1, idLink: 1},
{id: 1, position: 3, type: 1, idLink: 2},
]
Any ideas ?
Here is one way to achieve your goal:
const data2 = [
{name: "toto", position: null},
{name: "tata", position: null},
{name: "titi", position: null},
{name: "tutu", position: null},
];
for(let [index, value] of data2.entries()) {
value.position = index;
}
console.log(data2);
You could use the forEach method:
const data = [
{name: "toto", position: null},
{name: "tata", position: null},
{name: "titi", position: null},
{name: "tutu", position: null},
];
data.forEach((elem,id) => {
elem.position = id;
})
console.log(data);
The array map method can supply an index to the callback function e.g.:
const characters = [ {id: 2, position: 1, type: 1, idLink: 3}, {id: 3, position: 2, type: 2, idLink: 1}, {id: 4, position: 3, type: 1, idLink: 1}, {id: 1, position: 0, type: 1, idLink: 2}, ];
const repositioned = characters.map(
(character, index) => ({ ...character, position: index })
);
console.log(repositioned);

How to reformat json as nested json by parent_id

Here is the Json array, Every object has is_parent and parent_id properties,
If and object has children objects then it's is_parent property is 1 and vice versa.
let list = [
{id: 4, name: 'dd', is_parent: 1, parent_id: 0},
/**/{id: 5, name: 'ee', is_parent: 0, parent_id: 4},
/**/{id: 6, name: 'ff', is_parent: 1, parent_id: 5},
/**//**/{id: 7, name: 'gg', is_parent: 0, parent_id: 6},
{id: 8, name: 'hh', is_parent: 1, parent_id: 0},
/**/{id: 9, name: 'ii', is_parent: 0, parent_id: 8},
{id: 10, name: 'jj', is_parent: 1, parent_id: 0},
/**/{id: 11, name: 'kk', is_parent: 1, parent_id: 10},
/**//**/{id: 12, name: 'll', is_parent: 1, parent_id: 11},
/**//**//**/{id: 13, name: 'mm', is_parent: 0, parent_id: 12},
],
I want to format above json array as below
let array = [
{
id: 4,
name: 'dd',
is_parent: 1,
parent_id: 0,
children: [
{id: 5, name: 'ee', is_parent: 0, parent_id: 4},
{
id: 6,
name: 'ff',
is_parent: 1,
parent_id: 5,
children: [
{id: 7, name: 'gg', is_parent: 0, parent_id: 6}
]
}
]
},
{
id: 8,
name: 'hh',
is_parent: 1,
parent_id: 0,
children: [
{id: 9, name: 'ii', is_parent: 0, parent_id: 8}
]
},
{
id: 10,
name: 'jj',
is_parent: 1,
parent_id: 0,
children: [
{
id: 11,
name: 'kk',
is_parent: 1,
parent_id: 10,
children: [
{
id: 12,
name: 'll',
is_parent: 1,
parent_id: 11,
children: [
{id: 13, name: 'mm', is_parent: 0, parent_id: 12}
]
}
]
}
]
},
]
I tried to do this by foreach loop inside another foreach loop but it didn't work
Try this.
let formattedJon = (data, root) => {
const t = {};
this.list.forEach(o => ((t[o.parent_id] ??= {}).children ??= []).push(Object.assign(t[o.id] ??= {}, o)));
return t[root].children;
},
data = {data: []},
result = Object.fromEntries(Object
.entries(data)
.map(([k, v]) => [k, getTree(v, '0')])
);
console.log(result);
i found the answer from Build tree array from flat array in javascript
'use strict';
const createDataTree = dataset => {
const hashTable = Object.create(null);
dataset.forEach(aData => hashTable[aData.id] = {...aData, children: []});
const dataTree = [];
dataset.forEach(aData => {
if(aData.parent_id) {
hashTable[aData.parent_id].children.push(hashTable[aData.id]);
}
else {
dataTree.push(hashTable[aData.id]);
}
});
console.log(JSON.stringify(dataTree, null ,4))
return dataTree;
};
const list = [
{id: 4, name: 'dd', is_parent: 1, parent_id: 0},
/**/{id: 5, name: 'ee', is_parent: 0, parent_id: 4},
/**/{id: 6, name: 'ff', is_parent: 1, parent_id: 5},
/**//**/{id: 7, name: 'gg', is_parent: 0, parent_id: 6},
{id: 8, name: 'hh', is_parent: 1, parent_id: 0},
/**/{id: 9, name: 'ii', is_parent: 0, parent_id: 8},
{id: 10, name: 'jj', is_parent: 1, parent_id: 0},
/**/{id: 11, name: 'kk', is_parent: 1, parent_id: 10},
/**//**/{id: 12, name: 'll', is_parent: 1, parent_id: 11},
/**//**//**/{id: 13, name: 'mm', is_parent: 0, parent_id: 12},
];
createDataTree(list);
I have modifed the code and taken example from here . This is really good example and works like a charm

Sort an array of objects based on key/value of array

I need to sort an array of objects. How can I sort this array based on key arrval[index]?
arr = [
{ id: 0, name: 'Name 1', arrval: [5, 3, 1] },
{ id: 1, name: 'Name 2', arrval: [6, 4, 3] },
{ id: 2, name: 'Name 3', arrval: [3, 2, 0] },
]
So if I want to sort it by arrval[1], ascending, the result should be:
arr = [
{ id: 2, name: 'Name 3', arrval: [3, 2, 0] },
{ id: 0, name: 'Name 1', arrval: [5, 3, 1] },
{ id: 1, name: 'Name 2', arrval: [6, 4, 3] },
]
I can already sort this if I sort it by id or name but I can't seem to make it work by arrval
Simple as that
const src = [
{ id: 0, name: 'Name 1', arrval: [5, 3, 1] },
{ id: 1, name: 'Name 2', arrval: [6, 4, 3] },
{ id: 2, name: 'Name 3', arrval: [3, 2, 0] },
]
const sortByKeyIdx = ([...arr], key, idx) =>
arr
.sort(({[key]:a}, {[key]:b}) =>
a[idx]-b[idx])
console.log(sortByKeyIdx(src, 'arrval', 1))
Just sort by arrval like you sort by id or name
arr.sort( (a, b) => a.arrval[0] - b.arrval[0] )
Just pass the arrval index and sort it like you normally would. You can try this solution:
const sort = index => {
const arr = [
{ id: 0, name: 'Name 1', arrval: [5, 3, 1] },
{ id: 1, name: 'Name 2', arrval: [6, 4, 3] },
{ id: 2, name: 'Name 3', arrval: [3, 2, 0] }
]
return arr.sort((x, y) => x.arrval[index] - y.arrval[index])
}
console.log(sort(2))
There is two way to do this
first:
let arr = [{
id: 0,
name: 'Name 1',
arrval: [5, 3, 1]
},
{
id: 1,
name: 'Name 2',
arrval: [6, 4, 3]
},
{
id: 2,
name: 'Name 3',
arrval: [3, 2, 0]
},
]
let index = 1
arr.sort((a, b) => {
if (a.arrval[index] < b.arrval[index]) return -1
else if (a.arrval[index] < b.arrval[index]) return 1
else return 0
})
console.log(arr)
second:
let arr = [{
id: 0,
name: 'Name 1',
arrval: [5, 3, 1]
},
{
id: 1,
name: 'Name 2',
arrval: [6, 4, 3]
},
{
id: 2,
name: 'Name 3',
arrval: [3, 2, 0]
},
]
let index = 1
arr.sort((a, b) => a.arrval[1] - b.arrval[1])
console.log(arr)
The accepted answer sorts by the first array value. If you need to be able to deal with ties, where then the second array value becomes decisive, ...etc, then use this:
const src = [
{ id: 0, name: 'Name 1', arrval: [5, 3, 3] },
{ id: 1, name: 'Name 2', arrval: [5, 3, 1] },
{ id: 2, name: 'Name 3', arrval: [5, 2, 0, 1] },
{ id: 3, name: 'Name 4', arrval: [5, 2, 0] },
]
src.sort(({arrval: a}, {arrval: b}) => {
for (let i = 0, len = Math.min(a.length, b.length); i < len; i++) {
if (a[i] !== b[i]) return a[i] - b[i];
}
return a.length - b.length;
});
console.log(src);

Combine two array into one while getting the sum of instances in second array

I have two arrays. The first array is this:
arrayOne = [
{id: 1, type: 'Animal', legs: 4},
{id: 2, type: 'Animal', legs: 2},
{id: 3, type: 'Animal', legs: 8},
{id: 4, type: 'Plant', legs: 0},
]
This is the second array:
arrayTwo = [
{typeId: 1, processing: 2},
{typeId: 1, processing: 3},
{typeId: 1, approved: 3},
{typeId: 1, approved: 2},
{typeId: 1, disapproved: 3},
{typeId: 1, disapproved: 2},
{typeId: 2, approved: 2},
{typeId: 2, disapproved: 1},
{typeId: 2, disapproved: 1},
{typeId: 3, approved: 2},
{typeId: 4, disapproved: 3},
]
If id of arrayOne is equal to typeId of arrayTwo, then append arrayTwo into arrayOne and sum up the number of processing, approved and disapproved. This is my desiredArray:
desiredArray = [
{id: 1, type: 'Animal', legs: 4, processing: 5, approved: 5, disapproved: 5},
{id: 2, type: 'Animal', legs: 2, approved: 2, disapproved: 2},
{id: 3, type: 'Animal', legs: 8, approved: 2},
{id: 4, type: 'Plant', legs: 0, disapproved: 3},
]
You can first reduce the second array and then map it to the first one:
const arrayOne = [{id: 1, type: 'Animal', legs: 4},{id: 2, type: 'Animal', legs: 2},{id: 3, type: 'Animal', legs: 8},{id: 4, type: 'Plant', legs: 0},];
const arrayTwo = [{typeId: 1, processing: 2},{typeId: 1, processing: 3},{typeId: 1, approved: 3},{typeId: 1, approved: 2},{typeId: 1, disapproved: 3},{typeId: 1, disapproved: 2},{typeId: 2, approved: 2},{typeId: 2, disapproved: 1},{typeId: 2, disapproved: 1},{typeId: 3, approved: 2},{typeId: 4, disapproved: 3},];
const reduced = arrayTwo.reduce((a,{typeId, ...rest})=>{
a[typeId] ??= {};
Object.entries(rest).forEach(([k,v])=>{
a[typeId][k] ??= 0;
a[typeId][k]+=v;
});
return a;
},{});
const result = arrayOne.map(o=>({...o, ...reduced[o.id]}));
console.log(result);
Let me code it for u.
arrayOne = [{
id: 1,
type: 'Animal',
legs: 4
},
{
id: 2,
type: 'Animal',
legs: 2
},
{
id: 3,
type: 'Animal',
legs: 8
},
{
id: 4,
type: 'Plant',
legs: 0
},
]
arrayTwo = [{
typeId: 1,
processing: 2
},
{
typeId: 1,
processing: 3
},
{
typeId: 1,
approved: 3
},
{
typeId: 1,
approved: 2
},
{
typeId: 1,
disapproved: 3
},
{
typeId: 1,
disapproved: 2
},
{
typeId: 2,
approved: 2
},
{
typeId: 2,
disapproved: 1
},
{
typeId: 2,
disapproved: 1
},
{
typeId: 3,
approved: 2
},
{
typeId: 4,
disapproved: 3
},
]
for (let x in arrayOne) {
for (let y of arrayTwo) {
if(arrayOne[x].id == y.typeId){
arrayOne[x] = {...arrayOne[x], ...y};
}
}
}
console.log(arrayOne)

how to guet maximum value of object with same name and Id

i have an array of objects like below
[
{value: 1, id: 1, name: "x"},
{value: 5, id: 1, name: "x"},
{value: 1, id: 1, name: "y"},
{value: 8, id: 1, name: "y"},
{value: 1, id: 2, name: "x"},
{value: 3, id: 2, name: "x"},
{value: 1, id: 2, name: "y"},
{value: 4, id: 2, name: "y"}
]
i want to guet the object with max value with the same "name" and "id"
and push it in a new array ,
the expected output is like this :
[
{value: 5, id: 1, name: "x"},
{value: 8, id: 1, name: "y"},
{value: 3, id: 2, name: "x"},
{value: 4, id: 2, name: "y"},
]
thank you
You can use reduce method to do this,
const data = [
{value: 1, id: 1, name: "x"},
{value: 5, id: 1, name: "x"},
{value: 1, id: 1, name: "y"},
{value: 8, id: 1, name: "y"},
{value: 1, id: 2, name: "x"},
{value: 3, id: 2, name: "x"},
{value: 1, id: 2, name: "y"},
{value: 4, id: 2, name: "y"}
]
const res = data.reduce((prev, curr) => {
const index = prev.findIndex((item) => item.id === curr.id && item.name === curr.name);
if(index > -1) {
const obj = prev[index];
if(obj.value < curr.value) {
prev[index] = {...obj, value: curr.value};
return prev;
}
}
prev.push(curr);
return prev;
}, []);
console.log(res);
Using Array.prototype.reduce, you can group that array using id_name key pair and store the maximum values as follows.
const input = [
{value: 1, id: 1, name: "x"},
{value: 5, id: 1, name: "x"},
{value: 1, id: 1, name: "y"},
{value: 8, id: 1, name: "y"},
{value: 1, id: 2, name: "x"},
{value: 3, id: 2, name: "x"},
{value: 1, id: 2, name: "y"},
{value: 4, id: 2, name: "y"}
];
const groupBy = input.reduce((acc, cur) => {
const key = `${cur.id}_${cur.name}`;
if (!acc[key]) {
acc[key] = cur;
}
if (acc[key].value < cur.value) {
acc[key].value = cur.value;
}
return acc;
}, {});
const output = Object.values(groupBy);
console.log(output);
Reduce is used to return a new value that is basically accumulator (adds on previous value) from all the items in the array. Here we can use it to group items using specific key. As you wrote you want to have items showing a record with biggest value having same id and name, these values can be taken as a key (lets look at them as composite private keys of this object).
On each iteration, we check if there is already an object with that key added to the list, if it wasn't we add the object we are now on (during iteration) or if it was already added if its value is smaller than the current object we are on. If the value is smaller, we override the object with the current one.
In the end, we use JS Object.values method that strips away the keys and returns only the values of the object.
const list = [
{value: 1, id: 1, name: "x"},
{value: 5, id: 1, name: "x"},
{value: 1, id: 1, name: "y"},
{value: 8, id: 1, name: "y"},
{value: 1, id: 2, name: "x"},
{value: 3, id: 2, name: "x"},
{value: 1, id: 2, name: "y"},
{value: 4, id: 2, name: "y"}
];
const groupedResults = list.reduce((result, currentObject) => {
const currentKey = currentObject.id + currentObject.name;
if (!result[currentKey] || result[currentKey].value < currentObject.value) { /* Here we check if object with certain key was assigned to previously or if it was is the value smaller than of the object that we are currently seeing */
result[currentKey] = Object.assign({}, currentObject) //We need to do copy of the object (it can be also done using object destructuring) in order to have a new object that will not be bound by reference with the original one
};
return result;
}, {});
const requestedList = Object.values(groupedResults);
console.log(requestedList)

Categories

Resources