I have this array of objects that I want to filter based on these rules:
If more than one object has team and day are the same, remove the objects with the lowest Level
but if more than one object with same lowest Level then remove the one with the lowest strength
if there's still more than one remove one with the lowest ability.
Thought about creating a new array after each condition, then, also tried to come up with a way of doing it with all the conditions, and I am so lost with it I don't know where I'm up to.
I also tried creating an empty array and pushing the objects that meet the rules into it, but ended up having to meet all the rules anyway and was still just as complicated.
const players = [
{ Level: 1, team: 'green', strength: 2, ability: 2, day: 1 },
{ Level: 1, team: 'red', strength: 2, ability: 2, day: 2 },
{ Level: 1, team: 'red', strength: 2, ability: 2, day: 3 },
{ Level: 1, team: 'silver', strength: 2, ability: 2, day: 3 },
{ Level: 1, team: 'red', strength: 2, ability: 2, day: 4 },
{ Level: 1, team: 'silver', strength: 2, ability: 2, day: 4 },
{ Level: 1, team: 'red', strength: 2, ability: 2, day: 5 },
{ Level: 1, team: 'silver', strength: 2, ability: 2, day: 5 },
{ Level: 2, team: 'red', strength: 2, ability: 4, day: 4 },
{ Level: 2, team: 'red', strength: 2, ability: 4, day: 5 },
{ Level: 1, team: 'silver', strength: 2, ability: 2, day: 1 },
{ Level: 1, team: 'silver', strength: 2, ability: 2, day: 2 },
{ Level: 2, team: 'red', strength: 2, ability: 3, day: 1 },
{ Level: 2, team: 'silver', strength: 2, ability: 3, day: 1 },
{ Level: 2, team: 'red', strength: 2, ability: 3, day: 2 },
{ Level: 2, team: 'silver', strength: 2, ability: 3, day: 2 },
{ Level: 2, team: 'red', strength: 2, ability: 4, day: 1 },
{ Level: 3, team: 'red', strength: 3, ability: 4, day: 1 },
{ Level: 3, team: 'red', strength: 3, ability: 4, day: 2 },
{ Level: 3, team: 'red', strength: 3, ability: 4, day: 3 }
];
The approach I followed:
The general approach I follow is to compare between two candidates instead of selecting the best one between multiple candidates. This approach looks easier for me to reason.
The process does:
Sort records by key (team + day) so same key records are adjacents and it is easy to iterate.
Using reduce compare the current element with the last element of the partial result to see which one is higher in priority. When we have a higher priority we update the last element of result, otherwise we add the current element to result.
Higher priority is true when for the same key the current has higher priority following your requirements. I like the way this logic is isolated and implemented in isCandidateHigherPriorytyThanResult function. If you need to change the logic it would quite easy to adapt.
const players=[{Level:1,team:"green",strength:2,ability:2,day:1},{Level:1,team:"red",strength:2,ability:2,day:2},{Level:1,team:"red",strength:2,ability:2,day:3},{Level:1,team:"silver",strength:2,ability:2,day:3},{Level:1,team:"red",strength:2,ability:2,day:4},{Level:1,team:"silver",strength:2,ability:2,day:4},{Level:1,team:"red",strength:2,ability:2,day:5},{Level:1,team:"silver",strength:2,ability:2,day:5},{Level:2,team:"red",strength:2,ability:4,day:4},{Level:2,team:"red",strength:2,ability:4,day:5},{Level:1,team:"silver",strength:2,ability:2,day:1},{Level:1,team:"silver",strength:2,ability:2,day:2},{Level:2,team:"red",strength:2,ability:3,day:1},{Level:2,team:"silver",strength:2,ability:3,day:1},{Level:2,team:"red",strength:2,ability:3,day:2},{Level:2,team:"silver",strength:2,ability:3,day:2},{Level:2,team:"red",strength:2,ability:4,day:1},{Level:3,team:"red",strength:3,ability:4,day:1},{Level:3,team:"red",strength:3,ability:4,day:2},{Level:3,team:"red",strength:3,ability:4,day:3}];
const key = (teamObj) =>{
return teamObj.team + teamObj.day
}
const sortByKey =
(a,b)=>{
if(key(a) > key(b)) return 1
else
if(key(a) < key(b)) return -1
else return 0
}
// return true if obj is {} otherwise is false
const isAnEmptyObject = (obj) => Object.keys(obj).length === 0 && obj.constructor === Object
const isCandidateHigherPriorytyThanResult =
(candidate, result) =>{
if(key(result) !== key(candidate)) return false
if(candidate.level > result.level) return true
if(candidate.level < result.level) return false
if(candidate.strength > result.strength) return true
if(candidate.strength < result.strength) return false
if(candidate.ability > result.ability) return true
return false
}
const reducer = (result, current) =>{
const resultLast = result[result.length -1]
// To accomodate for first iteration were result value is: {[]}
if(isAnEmptyObject(resultLast))
{
result[result.length -1]= {...current}
return result
}
if(isCandidateHigherPriorytyThanResult(current, resultLast))
{
result[result.length -1] = {...current}
return result
}else
{
return result.concat(current)
}
}
const teamsResult =
players
.sort(sortByKey) //?
.reduce(reducer,[{}])
console.log(teamsResult)
Group players by team and ``day
Iterate over each category:
If only one player in the category, keep it
Else, define a set of filtering strategy to use it according to the rules you mentioned
Get the number of players with the lowest level in this category, if only one player found, set filtering condition to remove it
Else, if more than one player found having the lowest level, get the count of those having the lowest strength among them
If only one player found in this result set filtering conditions to remove it
Else, set the filtering conditions to remove the players with the lowest ability among them
At the end of each iteration, you will need to filter out the players according to the filtering conditions. Lastly, return the remaining players in each category in one list.
const players = [
{ Level: 1, team: 'green', strength: 2, ability: 2, day: 1 },
{ Level: 1, team: 'red', strength: 2, ability: 2, day: 2 },
{ Level: 1, team: 'red', strength: 2, ability: 2, day: 3 },
{ Level: 1, team: 'silver', strength: 2, ability: 2, day: 3 },
{ Level: 1, team: 'red', strength: 2, ability: 2, day: 4 },
{ Level: 1, team: 'silver', strength: 2, ability: 2, day: 4 },
{ Level: 1, team: 'red', strength: 2, ability: 2, day: 5 },
{ Level: 1, team: 'silver', strength: 2, ability: 2, day: 5 },
{ Level: 2, team: 'red', strength: 2, ability: 4, day: 4 },
{ Level: 2, team: 'red', strength: 2, ability: 4, day: 5 },
{ Level: 1, team: 'silver', strength: 2, ability: 2, day: 1 },
{ Level: 1, team: 'silver', strength: 2, ability: 2, day: 2 },
{ Level: 2, team: 'red', strength: 2, ability: 3, day: 1 },
{ Level: 2, team: 'silver', strength: 2, ability: 3, day: 1 },
{ Level: 2, team: 'red', strength: 2, ability: 3, day: 2 },
{ Level: 2, team: 'silver', strength: 2, ability: 3, day: 2 },
{ Level: 2, team: 'red', strength: 2, ability: 4, day: 1 },
{ Level: 3, team: 'red', strength: 3, ability: 4, day: 1 },
{ Level: 3, team: 'red', strength: 3, ability: 4, day: 2 },
{ Level: 3, team: 'red', strength: 3, ability: 4, day: 3 }
];
const _groupPlayersByTeamAndDay = (players=[]) =>
players.reduce((acc,item) => {
const { team, day } = item;
const key = `${team}${day}`;
const prev = acc[key];
if(!prev) acc[key] = [item];
else acc[key].push(item);
return acc;
}, {});
const _getLowestValueOfAttr = (list=[], attr) =>
list.reduce((acc,item) =>
item[attr] < acc ? item[attr] : acc, Number.MAX_VALUE);
const _countItemsWithKeyValuePairs = (list=[], keys=[], values=[]) =>
list.reduce((acc,item) =>
keys.every((key,i) => item[key]===values[i]) ? acc+1 : acc, 0);
const _matchesFilterConditions = (filterConditions={}, player={}) =>
Object.entries(filterConditions).every(([key,value]) => player[key]===value);
const _filterArrHelper = (teamDayPlayersMap={}) =>
Object.values(teamDayPlayersMap).map(teamDayPlayersList => {
let list = [...teamDayPlayersList];
if(list.length <= 1) return list;
let filterConditions = {};
const lowestLevel = _getLowestValueOfAttr(list, 'Level');
const playersWithLowestLevel = _countItemsWithKeyValuePairs(list, ['Level'], [lowestLevel]);
if(playersWithLowestLevel <= 1) {
//remove the player with lowest Level
filterConditions = { Level: lowestLevel };
} else {
const lowestStrength = _getLowestValueOfAttr(list, 'strength');
const playersWithLowestStrength = _countItemsWithKeyValuePairs(list, ['Level','strength'], [playersWithLowestLevel,lowestStrength]);
if(playersWithLowestStrength <= 1){
//remove the player with lowest strength
filterConditions = { Level: lowestLevel, strength: lowestStrength };
} else {
//remove the players with the lowest ability
const lowestAbility = _getLowestValueOfAttr(list, 'ability');
filterConditions = { Level: lowestLevel, strength: lowestStrength, ability: lowestAbility };
}
}
return list.filter(player =>
!_matchesFilterConditions(filterConditions, player));
}).flat();
const filterArr = (players=[]) => {
const teamDayPlayersMap = _groupPlayersByTeamAndDay(players);
return _filterArrHelper(teamDayPlayersMap);
}
console.log( filterArr(players) );
Related
This question builds on many similar ones like Construct hierarchy tree from flat list with parent field?
However the twist is that there is no parent id.
e.g.
[
{id: 1, depth: 1, ...},
{id: 2, depth: 2, ...},
{id: 3, depth: 3, ...},
{id: 4, depth: 2, ...},
{id: 5, depth: 1, ...},
{id: 6, depth: 2, ...},
{id: 7, depth: 2, ...},
{id: 8, depth: 1, ...},
{id: 9, depth: 2, ...},
{id: 10, depth: 3, ...},
{id: 11, depth: 3, ...},
]
What is a performant way to construct the following tree?
Note that the children always come after the parent i.e. one can see the tree from the depth value. For example, id 2 is a child of id 1 since its depth is 2 and id 1 has a depth of 1. id 3 is a child of id 2 since id 3 has a depth of 3. id 4 is a child of id 1 not id 3 because id 4 has a depth of 2 (a step up) from id 3's depth of 3
\\tree digram
1
2
3
4
5
6
7
8
9
10
11
Should have values like
[
{id:1, depth:1, children: [
{id: 2, depth: 2, children: [...]},
...
]},
{id:5, depth:1, children: [...]},
{id:6, depth:1, children: [...]},
]
You can use an array for this that has an index for each depth. At every moment, it will represent a path from the (virtual) root to the current node. When dealing with a node, its parent will sit at index depth-1, where it can be inserted in that parent's children property, and the node itself will be placed at index depth:
function createForest(flatdata) {
const path = [{ children: [] }];
for (const obj of flatdata) {
path[obj.depth - 1].children.push(path[obj.depth] = { ...obj, children: [] });
}
return path[0].children;
}
// demo
const flatdata = [{id: 1, depth: 1},{id: 2, depth: 2},{id: 3, depth: 3},{id: 4, depth: 2},{id: 5, depth: 1},{id: 6, depth: 2},{id: 7, depth: 2},{id: 8, depth: 1},{id: 9, depth: 2},{id: 10, depth: 3},{id: 11, depth: 3}];
const roots = createForest(flatdata);
console.log(roots);
Irregular depths
If the depth values do not correspond to the actual depth of the nodes, but leave gaps, then use a "dictionary" (a plain object) to record the mapping of the depth property values with which real depth they correspond with:
function createForest(flatdata) {
const path = [{ children: [] }];
const depthMap = { 0: 0 };
for (const obj of flatdata) {
path[(depthMap[obj.depth] ??= path.length) - 1].children.push(
path[depthMap[obj.depth]] = { ...obj, children: []}
);
}
return path[0].children;
}
// demo
const flatdata = [{id: 1, depth: 10},{id: 2, depth: 20},{id: 3, depth: 30},{id: 4, depth: 20},{id: 5, depth: 10},{id: 6, depth: 20},{id: 7, depth: 20},{id: 8, depth: 10},{id: 9, depth: 20},{id: 10, depth: 30},{id: 11, depth: 30}];
const roots = createForest(flatdata);
console.log(roots);
If however, the only irregularity is that the depth does not always start at 1, but sometimes at 2, it will be more efficient to prefix the input data with a dummy depth-one node, use the first function, and then remove the dummy "root" (with depth 1) from the result.
Go through the array and add each item to the tree as well as to a trail of breadcrumbs. Each next item either goes as a child to the last one or you backtrack through the breadcrumb trail to the correct depth where it needs to be inserted:
const peek = arr =>
arr[arr.length-1];
function toTree(arr) {
const tree = [];
const trail = [];
for (const item of arr) {
while ((peek(trail)?.depth ?? 0) >= item.depth) {
trail.pop();
}
const current = peek(trail)?.children ?? tree;
const treeNode = {...item, children: []};
current.push(treeNode);
trail.push(treeNode);
}
return tree;
}
const array = [
{id: 1, depth: 1, },
{id: 2, depth: 2, },
{id: 3, depth: 3, },
{id: 4, depth: 2, },
{id: 5, depth: 1, },
{id: 6, depth: 2, },
{id: 7, depth: 2, },
{id: 8, depth: 1, },
{id: 9, depth: 2, },
{id: 10, depth: 3 },
{id: 11, depth: 3 },
]
console.log(toTree(array));
This solution clones each item, in order to add the .children property. If no cloning is necessary, item can be directly mutated.
You could take an array of the last inserted objects.
const
data = [{ id: 1, depth: 1 }, { id: 2, depth: 2 }, { id: 3, depth: 3 }, { id: 4, depth: 2 }, { id: 5, depth: 1 }, { id: 6, depth: 2 }, { id: 7, depth: 2 }, { id: 8, depth: 1 }, { id: 9, depth: 2 }, { id: 10, depth: 3 }, { id: 11, depth: 3 }],
result = data.reduce((r, { depth, ...o }) => {
r[depth - 1].push({ ...o, children: r[depth] = [] });
return r;
}, [[]])[0];
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
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);
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)
The data structure that I am trying to achieve would look as so :
I would like the list_id to become a key in a object, and hold all the id's of the items that have the matching list id.
var lists = { (list_id)1 : [1, 2, 3]
(list_id)2 : [4, 5, 6]
(list_id)3 : [7, 8, 9]
(list_id)4 : [10, 11, 12] };
this object is created from a json data structure that looks like this:
let json = [{ id: 1, list_id: 1 }, { id: 2, list_id: 1 },
{id: 3, list_id: 1 }, {id: 4, list_id: 2 },
{id: 5, list_id: 2 }, {id: 6, list_id: 2 },
{id: 7, list_id: 3 }, {id: 8, list_id: 3 },
{id: 9, list_id: 3 }, {id: 10, list_id: 4 },
{id: 11, list_id: 4 }, {id: 12, list_id: 4 }]
I can make an object that holds all the list_id's as keys but am getting stumped on pushing the actions_id into the value pair array with the matching list id.
let listAll = {};
json.forEach(function(lista, index, listb) {
listAll[lista.list_id] = [];
if ( listAll[lista.list_id] === lista.list_id){
listAll[lista.list_id].push(lista.id)
} else {
listAll[lista.list_id] = [lista.id];
}
});
My goal is to have and object that contains a key for every list_id currently avaliable from the actions.
Then add every action that contains the matching list_id into a value pair array.
the current output of this code is
{ '1': [ 3 ], '2': [ 6 ], '3': [ 9 ], '4': [ 12 ] }
which does not contain all numbers, each array should contain 3 numbers.
An alternative is using the function reduce to group the objects by a specific key = ['list_id', list_id].join('').
let json = [{ id: 1, list_id: 1 }, { id: 2, list_id: 1 }, {id: 3, list_id: 1 }, {id: 4, list_id: 2 }, {id: 5, list_id: 2 }, {id: 6, list_id: 2 }, {id: 7, list_id: 3 }, {id: 8, list_id: 3 }, {id: 9, list_id: 3 }, {id: 10, list_id: 4 }, {id: 11, list_id: 4 }, {id: 12, list_id: 4 }],
result = json.reduce((a, {id, list_id}) => {
let key = ['list_id', list_id].join(''); // For example: this is creating ['list_id', 1] to list_id1
(a[key] || (a[key] = [])).push(id);
return a;
}, Object.create(null)/*This is only to create an object without prototype -> {}*/);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Why don't you try hasOwnProperty instead?
var listAll = {};
json.forEach(function(list, index) {
if (listAll.hasOwnProperty(list.list_id)) {
listAll[list.list_id].push(list.id);
}else {
listAll[list.list_id] = [list.id];
}
});
console.log(listAll);
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));