Assigning a grouped index within a javascript array - javascript

How would I assign an index to each object within a group in an array using javascript. So starting at zero and counting up inside each group, ordered by the group then the id.
Starting with this
let data = [
{ "id": "AB", "name": "Fred", "group": 1},
{ "id": "BC", "name": "Jane", "group": 2 },
{ "id": "CD", "name": "Mary", "group": 1 },
{ "id": "DE", "name": "Bob", "group": 2 },
{ "id": "EF", "name": "Chris", "group": 1 },
{ "id": "FG", "name": "Steve", "group": 2 },
{ "id": "GH", "name": "Jim", "group": 2 }
]
But adding the groupIndex field for each object.
dataGrouped = [
{ "id": "DE", "name": "Bob", "group": 2, "groupIndex": 1 },
{ "id": "EF", "name": "Chris", "group": 1, "groupIndex": 2 },
{ "id": "BC", "name": "Jane", "group": 2, "groupIndex": 0 },
{ "id": "FG", "name": "Steve", "group": 2, "groupIndex": 2 },
{ "id": "AB", "name": "Fred", "group": 1, "groupIndex": 0},
{ "id": "CD", "name": "Mary", "group": 1, "groupIndex": 1 },
{ "id": "GH", "name": "Jim", "group": 2, "groupIndex": 3 }
]

You could take an object for the indices.
const
indices = {},
data = [{ id: "AB", name: "Fred", group: 1 }, { id: "BC", name: "Jane", group: 2 }, { id: "CD", name: "Mary", group: 1 }, { id: "DE", name: "Bob", group: 2 }, { id: "EF", name: "Chris", group: 1 }, { id: "FG", name: "Steve", group: 2 }, { id: "GH", name: "Jim", group: 2 }],
result = data.map(o => {
indices[o.group] ??= 0;
return { ...o, groupIndex: indices[o.group]++ };
});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Presented below is one possible way to achieve the desired objective (viz, assigning group index to array elements). This solution does not address: "ordered by the group then the id."
Code Snippet
// method to assign group-index
// the resulting array may get ordered based on group & group-index (ascending)
const assignGroupIndex = arr => (
Object.values(
arr?.reduce(
(acc, { group, ...rest }) => {
if (!(group in acc)) acc[group] = [];
acc[group].push({
group, ...rest, groupIndex: acc[group].length,
});
return acc;
},
{}
)
).flat()
);
/*
// method to assign group-index for each elt in array
const assignGroupIndex = arr => ( // arg "arr" is the input array
// extract the values from the intermediate result object generated below
Object.values(
// use ".reduce()" to iterate the array and generate an intermediate result
arr?.reduce(
// "acc" is the accumulator, destructure to access "group" & other props
(acc, { group, ...rest }) => {
// if "group" not already in accumulator, set the value as empty array
if (!(group in acc)) acc[group] = [];
// push to the array a new object with "groupIndex" as current length
acc[group].push({
group, ...rest, groupIndex: acc[group].length,
});
// always return the accumulator for each iteration of ".reduce()"
return acc;
},
{} // "acc" - the accumulator is initialized as empty object
)
).flat() // use ".flat()" to remove nested arrays from Object.values()
);
*/
let data = [
{ "id": "AB", "name": "Fred", "group": 1},
{ "id": "BC", "name": "Jane", "group": 2 },
{ "id": "CD", "name": "Mary", "group": 1 },
{ "id": "DE", "name": "Bob", "group": 2 },
{ "id": "EF", "name": "Chris", "group": 1 },
{ "id": "FG", "name": "Steve", "group": 2 },
{ "id": "GH", "name": "Jim", "group": 2 }
];
console.log(
'assigned group-index to array:\n',
assignGroupIndex(data)
);
.as-console-wrapper { max-height: 100% !important; top: 0 }
Explanation
Inline comments added to the snippet above.

let data = [
{ "id": "AB", "name": "Fred", "group": 1},
{ "id": "BC", "name": "Jane", "group": 2 },
{ "id": "CD", "name": "Mary", "group": 1 },
{ "id": "DE", "name": "Bob", "group": 2 },
{ "id": "EF", "name": "Chris", "group": 1 },
{ "id": "FG", "name": "Steve", "group": 2 },
{ "id": "GH", "name": "Jim", "group": 2 }
];
let groupCounts = {};
let dataGrouped = data.map(i=>({
...i,
groupIndex: groupCounts[i.group] = ++groupCounts[i.group] || 0
})).sort((a,b)=>a.group-b.group || a.id.localeCompare(b.id));
console.log(dataGrouped);

Related

how to transform array of object using reduce of map?

I am trying to transform array of object to different format.I also take reference from this url
Group js objects by multiple properties
But not able to solve the problem
I have a input array of object which have class and sections I need to transform or filter student in different format on basis of class and section.
here is my code
https://jsbin.com/nidudisuza/edit?js,output
let expectedout = [
{
class: 1,
sections: [{
"section": "B",
students: [ {
"name": "Test",
"class": 1,
"gender": "M",
"section": "B",
"rollNumber": "123111",
"sports": [
"Badminton",
"Chess"
],
"age": 7
}]
}]
},
{
class: 3,
sections: [{
"section": "B",
students: [{
"name": "Rahul",
"class": 3,
"gender": "M",
"section": "B",
"rollNumber": "1231",
"sports": [
"Badminton",
"Chess"
],
"age": 7
}]
}]
},
{
class: 5,
sections: [{
"section": "C",
students: [
{
"name": "Rajat",
"class": 5,
"gender": "M",
"section": "C",
"rollNumber": "123122",
"sports": [
"Chess"
],
"age": 9
}
]
}]
}
]
const input = [{
"name": "Rahul",
"class": 3,
"gender": "M",
"section": "B",
"rollNumber": "1231",
"sports": [
"Badminton",
"Chess"
],
"age": 7
},
{
"name": "Rajat",
"class": 5,
"gender": "M",
"section": "C",
"rollNumber": "123122",
"sports": [
"Chess"
],
"age": 9
},
{
"name": "Test",
"class": 1,
"gender": "M",
"section": "B",
"rollNumber": "123111",
"sports": [
"Badminton",
"Chess"
],
"age": 7
},
]
function abc() {
// let output =
// data.map((i) => {
// let obj = {};
// obj.class = i.class;
// obj.sections = [];
// obj.sections.push({
// section:obj.section,
// students:[obj]
// })
// return obj;
// })
let output =input.reduce((acc,i)=>{
let cls = i.class;
const found = acc.some(el => el.class === cls);
let obj = {
section:i.section,
students:[]
}
found.sections.students.push[i]
},[])
return output
}
console.log(abc())
}
I'd go for a 2 step process:
Reorganize the values to be mapped by an index (both for class and sections)
Then flatten out everything in arrays; depending on what you're doing having a dictionary could also be convenient.
Below you can find the function that makes the dictionary, in the snippet there is also the second step that transforms the dictionary values in arrays.
function abc() {
let output = input.reduce((ac, x) => {
let keyCl = x.class
let keySec = x.section
let orgClass = ac[keyCl]
let sections = orgClass && orgClass.sections || {}
let oldSec = sections && sections[keySec]
let sectionBase = oldSec || {
section: keySec,
students: []
}
sectionBase.students = [...(oldSec ? .students || []), x]
return {
...ac,
// we organize classes to be indexed in an obj,
// we can flat this out later
[keyCl]: {
class: keyCl,
sections: {
...sections,
// like for classes we organize sections by key
[keySec]: sectionBase
}
}
}
}, {})
return output
}
let expectedout = [{
class: 1,
sections: [{
"section": "B",
students: [{
"name": "Test",
"class": 1,
"gender": "M",
"section": "B",
"rollNumber": "123111",
"sports": [
"Badminton",
"Chess"
],
"age": 7
}]
}]
},
{
class: 3,
sections: [{
"section": "B",
students: [{
"name": "Rahul",
"class": 3,
"gender": "M",
"section": "B",
"rollNumber": "1231",
"sports": [
"Badminton",
"Chess"
],
"age": 7
}]
}]
},
{
class: 5,
sections: [{
"section": "C",
students: [
{
"name": "Rajat",
"class": 5,
"gender": "M",
"section": "C",
"rollNumber": "123122",
"sports": [
"Chess"
],
"age": 9
}
]
}]
}
]
const input = [{
"name": "Rahul",
"class": 3,
"gender": "M",
"section": "B",
"rollNumber": "1231",
"sports": [
"Badminton",
"Chess"
],
"age": 7
},
{
"name": "Rajat",
"class": 5,
"gender": "M",
"section": "C",
"rollNumber": "123122",
"sports": [
"Chess"
],
"age": 9
},
{
"name": "Test",
"class": 1,
"gender": "M",
"section": "B",
"rollNumber": "123111",
"sports": [
"Badminton",
"Chess"
],
"age": 7
},
]
function abc() {
let output = input.reduce((ac, x) => {
let keyCl = x.class
let keySec = x.section
let orgClass = ac[keyCl]
let sections = orgClass && orgClass.sections || {}
let oldSec = sections && sections[keySec]
let sectionBase = oldSec || {
section: keySec,
students: []
}
sectionBase.students = [...(oldSec && oldSec.students || []), x]
return {
...ac,
// we organize classes to be indexed in an obj,
// we can flat this out later
[keyCl]: {
class: keyCl,
sections: {
...sections,
// like for classes we organize sections by key
[keySec]: sectionBase
}
}
}
}, {})
return output
}
let res = abc()
console.log('with keys', res)
let onlyValues = Object.values(res).map(x => ({ ...x,
sections: Object.values(x.sections)
}))
console.log('only values', onlyValues)
Using Array#reduce to build an Object with the collected data. Foreach object look in the accumulated object if there exists an prperty for this class. If not create one and add to this the groundstructure for this class.
Afterwards add in both cases to the students array a new entry for this student.
After creating by this the object use Object#entries to take only the values of the object to get the ished array.
function modify(data) {
let res= Object.values(data.reduce((acc, cur) => {
if (!acc[cur.class]) {
acc[cur.class] = {
class: cur.class,
sections: [{
"section": cur.section,
students: []
}]
};
}
acc[cur.class].sections[0].students.push(cur);
return acc;
},{}));
return res;
}
const data = [{
"name": "Rahul",
"class": 3,
"gender": "M",
"section": "B",
"rollNumber": "1231",
"sports": [
"Badminton",
"Chess"
],
"age": 7
},
{
"name": "Rajat",
"class": 5,
"gender": "M",
"section": "C",
"rollNumber": "123122",
"sports": [
"Chess"
],
"age": 9
},
{
"name": "Test",
"class": 1,
"gender": "M",
"section": "B",
"rollNumber": "123111",
"sports": [
"Badminton",
"Chess"
],
"age": 7
},
]
console.log(modify(data));

Creating a new array from the parents of array child element

I have the following array
{
"id": "111",
"name": "1111",
"children": [
{
"id": "22222",
"name": "2222",
"children": [
{
"id": "AAAA",
"name": "AAAA",
"children": [
{
"id": "DDD",
"name": "DDD"
},
{
"id": "EEE",
"name": "EEE"
}
]
},
{
"id": "BBBB",
"name": "BBB",
"children": [
{
"id": "FFF",
"name": "FFF"
},
{
"id": "GGG",
"name": "GGG",
"children": [
{
"id": "7777",
"name": "7777"
},
{
"id": "8888",
"name": "8888"
}
]
}
]
}
]
}
]
}
And I would like to create an array with the parents of a child by its ID.
So for example if I wanted to get the path to the child with ID "FFF", then the array would look like something like this:
["1111", "2222", "BBB", "FFF"]
How could I go about doing that?
You could take an iterative and recursive approach.
function getItems({ children, ...object }, key, value) {
var temp;
if (object[key] === value) return [object];
if (children) children.some(o => temp = getItems(o, key, value));
return temp && [object, ...temp];
}
var data = { id: "111", name: "1111", children: [{ id: "22222", name: "2222", children: [{ id: "AAAA", name: "AAAA", children: [{ id: "DDD", name: "DDD" }, { id: "EEE", name: "EEE" }] }, { id: "BBBB", name: "BBB", children: [{ id: "FFF", name: "FFF" }, { id: "GGG", name: "GGG", children: [{ id: "7777", name: "7777" }, { id: "8888", name: "8888" }] }] }] }] };
console.log(getItems(data, 'id', 'FFF'));
.as-console-wrapper { max-height: 100% !important; top: 0; }
You could implement a recursive search to find all paths and return the correct one when you reach the desired name-value pair.
const isObject = (obj) => obj === Object(obj);
let data = loadData();
let expected = [ '1111', '2222', 'BBB', 'FFF' ];
let actual = findPath(data, 'name', 'FFF');
console.log(JSON.stringify(expected) === JSON.stringify(actual));
function findPath(data, key, value, includeIndicies=false) {
let opts = { found : null, includeIndicies : includeIndicies };
findPathInternal(data, key, value, opts, []);
return opts.found;
}
function findPathInternal(node, key, val, opts, path) {
if (Array.isArray(node)) {
for (let i = 0; i < node.length; i++) {
findPathInternal(node[i], key, val, opts, opts.includeIndicies ? path.concat(i) : path);
}
} else if (isObject(node)) {
if (node[key] === val) {
opts.found = path.concat(val); return; // Exit
} else {
let keys = Object.keys(node);
for (let i = 0; i < keys.length; i++) {
findPathInternal(node[keys[i]], key, val, opts, path.concat(node[key]));
}
}
}
};
function loadData() {
return {
"id": "111",
"name": "1111",
"children": [{
"id": "22222",
"name": "2222",
"children": [{
"id": "AAAA",
"name": "AAAA",
"children": [{
"id": "DDD",
"name": "DDD"
},
{
"id": "EEE",
"name": "EEE"
}
]
},
{
"id": "BBBB",
"name": "BBB",
"children": [{
"id": "FFF",
"name": "FFF"
},
{
"id": "GGG",
"name": "GGG",
"children": [{
"id": "7777",
"name": "7777"
},
{
"id": "8888",
"name": "8888"
}
]
}
]
}
]
}]
};
}
.as-console-wrapper { top: 0; max-height: 100% !important; }

Map Json Data to new key-value pair JavaScript object

I want to map one json data to a new javascript object like following. Here the json data is dynamic and can have more files with more users. The group information is new and it depends on parent-child information. Can anyone please help me out? Thank you for your time.
Before:
{
"userinfo": {
"/home/user/main/sub/info/1stfile.txt": {
"John": "something",
"Mike": "something",
"Merry": "something",
"Susan": "something"
},
"/home/user/main/info/2ndfile.txt": {
"Mulan": "something",
"James": "something"
},
"/home/user/main/info/3rdfile.txt": {
"Nancy": "something"
},
"/home/user/main/4thfile.txt": {
"Kamal": "something",
"Xian": "something",
"Mila": "something"
}
}
}
After:
{
"name": "main",
"children": [
{
"name": "1stfile",
"children": [
{
"name": "John",
"group": "1"
},
{
"name": "Mike",
"group": "1"
},
{
"name": "Merry",
"group": "1"
},
{
"name": "Susan",
"group": "1"
}
],
"group": 1
},
{
"name": "2ndfile",
"children": [
{
"name": "Mulan",
"group": 2
},
{
"name": "James",
"group": 2
}
],
"group": 2
},
{
"name": "3rdfile",
"children": [
{
"name": "Nancy",
"group": 3
}
],
"group": 3
},
{
"name": "4thfile",
"children": [
{
"name": "Kamal",
"group": 4
},
{
"name": "Xian",
"group": 4
},
{
"name": "Mila",
"group": 4
}
],
"group": 4
}
],
"group": 0
}
I was trying to build one block of parent-child by using following code
var jsonData = json["userinfo"];
var keys = Object.keys(jsonData);
console.log(keys);
let data = {};
for (var j = 0; j < keys.length; j++) {
let g = 1;
data[j] = { name: keys[j], group: g++ };
}
console.log(data);
Which is giving following output
{
0: {
"name": "/home/user/main/sub/info/1stfile.txt",
"group": 1
},
1: {
"name": "/home/user/main/info/2ndfile.txt",
"group": 1
},
2: {
"name": "/home/user/main/info/3rdfile.txt",
"group": 1
},
3: {
"name": "/home/user/main/4thfile.txt",
"group": 1
}
}
The value is assigning properly but is creating extra keys (0,1,2,3)!
Assuming you need something like this.
You can utilize Array.map() and Object.keys() function for your operation.
<script>
const beforeJSON = `{
"userinfo": {
"/home/user/main/sub/info/1stfile.txt": {
"John": "something",
"Mike": "something",
"Merry": "something",
"Susan": "something"
},
"/home/user/main/info/2ndfile.txt": {
"Mulan": "something",
"James": "something"
},
"/home/user/main/info/3rdfile.txt": {
"Nancy": "something"
},
"/home/user/main/4thfile.txt": {
"Kamal": "something",
"Xian": "something",
"Mila": "something"
}
}
}`
const before = JSON.parse(beforeJSON);
const filenames = Object.keys(before.userinfo);
const after = {
name: 'main',
children: [],
group: 0,
}
const children = filenames.map((filename, idx) => {
const innerChildren = Object.keys(before.userinfo[filename]).map((n) => ({
name: n,
group: idx + 1,
}))
return ({
name: filename,
children: innerChildren,
group: idx + 1,
});
})
after.children = children;
console.log(after);
</script>
Please format your code next time before posting another question.

How to flatten and merge nested array from each of child array

So i have this data:
let data = [
{
"purchase_id": 1,
"product": [
{
"name": "A",
"id": 1,
"transactions": [
{
"price": 5,
"qty": 2
},
{
"price": 10,
"qty": 2
}
]
},
{
"name": "B",
"id": 2,
"transactions": [
{
"price": 3,
"qty": 4
}
]
}
]
},
{
"purchase_id": 2,
"product": [
{
"name": "C",
"id": 3,
"transactions": [
{
"price": 5,
"qty": 2
}
]
},
{
"name": "D",
"id": 4,
"transactions": [
{
"price": 3,
"qty": 4
}
]
}
]
}
]
And i want to flatten array from each data.product.transactions data:
"transactions": [
{
"price",
"qty"
}
]
Expected output is:
[
{
"purchase_id": 1,
"name": "A",
"id": 1,
"price": 5,
"qty": 2
},
{
"purchase_id": 1,
"name": "A",
"id": 1,
"price": 10,
"qty": 2
},
{
"purchase_id": 1,
"name": "B",
"id": 2,
"price": 3,
"qty": 4
},
{
"purchase_id": 2,
"name": "C",
"id": 3,
"price": 5,
"qty": 2
},
{
"purchase_id": 2,
"name": "D",
"id": 4,
"price": 3,
"qty": 4
},
]
I have tried to use object assign, reduce but my code doesn't work. Thank you
Use nested Array.map() to create the objects, and spread into Array.concat() to flatten the sub-arrays at each level:
const data = [{"purchase_id":1,"product":[{"name":"A","id":1,"transactions":[{"price":5,"qty":2},{"price":10,"qty":2}]},{"name":"B","id":2,"transactions":[{"price":3,"qty":4}]}]},{"purchase_id":2,"product":[{"name":"C","id":3,"transactions":[{"price":5,"qty":2}]},{"name":"D","id":4,"transactions":[{"price":3,"qty":4}]}]}];
const result = [].concat(...data.map(({ purchase_id, product }) =>
[].concat(...product.map(({ name, id, transactions }) =>
transactions.map((o) => ({
purchase_id,
name,
id,
...o
})
)))));
console.log(result);
If you want to avoid the temp sub-arrays, and the flattering, use nested Array.forEach() calls, and push the created objects to a predefined array:
const data = [{"purchase_id":1,"product":[{"name":"A","id":1,"transactions":[{"price":5,"qty":2},{"price":10,"qty":2}]},{"name":"B","id":2,"transactions":[{"price":3,"qty":4}]}]},{"purchase_id":2,"product":[{"name":"C","id":3,"transactions":[{"price":5,"qty":2}]},{"name":"D","id":4,"transactions":[{"price":3,"qty":4}]}]}];
const result = [];
data.forEach(({ purchase_id, product }) =>
product.forEach(({ name, id, transactions }) =>
transactions.forEach((o) => result.push({
purchase_id,
name,
id,
...o
})
)));
console.log(result);
var arr = [];
data.forEach(x => {
x.product.forEach(y => {
y.transactions.forEach(z => {
z["name"] = y.name;
z["id"] = y.id;
z["purchase_id"] = x.purchase_id;
arr.push(z);
});
})
});
console.log(arr);

API end point output JSON filter in AngularJS

I have an end point API. There are four objects with keys, id, group, name and date_modified. I am using AngularJS 1 to call the API and print the output. Now, there are few multiple entries, where group and name are same but date_modified is different.
To show the output, I want to filter it in a way that if group and name are same in an object then it will take the object where date_modified value is highest and ignore the others.
End point API JSON structure :-
[
{
"id": 1,
"group": "AB",
"name": "John",
"date_modified": "2018-01-30T12:01:47Z"
},
{
"id": 2,
"group": "BC",
"name": "Alex",
"date_modified": "2018-01-30T12:04:43Z"
},
{
"id": 3,
"group": "AB",
"name": "John",
"date_modified": "2018-01-29T12:00:02Z"
},
{
"id": 4,
"group": "CD",
"name": "Peter",
"date_modified": "2018-01-29T12:00:07Z"
}
]
services.js :-
app.factory('Instlistdata', function($resource) {
var list_data = 'http://127.0.0.1:8000/lists/?format=json' ;
return $resource(list_data, {}, {
query: {
method: 'GET',
isArray:true,
}
});
});
Controller.js :-
app.controller('dasboardController', ['$rootScope', '$scope', '$http', '$window', 'Instlistdata', function($rootScope, $scope, $http, $window, Instlistdata) {
$scope.lists = [];
Instlistdata.query({},function(data) {
$scope.lists = data;
console.log(data);
});
}]);
Now, it is giving me the JSON output in console. But I want filtered output, i.e
[
{
"id": 1,
"group": "AB",
"name": "John",
"date_modified": "2018-01-30T12:01:47Z"
},
{
"id": 2,
"group": "BC",
"name": "Alex",
"date_modified": "2018-01-30T12:04:43Z"
},
{
"id": 3,
"group": "CD",
"name": "Peter",
"date_modified": "2018-01-29T12:00:07Z"
}
]
"date_modified": "2018-01-30T12:01:47Z" is higher that the "date_modified": "2018-01-29T12:00:02Z" where "group" : "AB" and "name" : "John" . You can see we ignored the other one.
You can achieve this using something like the following code:
var array = [
{
"id": 1,
"group": "AB",
"name": "John",
"date_modified": "2018-01-30T12:01:47Z"
},
{
"id": 2,
"group": "BC",
"name": "Alex",
"date_modified": "2018-01-30T12:04:43Z"
},
{
"id": 3,
"group": "AB",
"name": "John",
"date_modified": "2018-01-29T12:00:02Z"
},
{
"id": 4,
"group": "CD",
"name": "Peter",
"date_modified": "2018-01-29T12:00:07Z"
}
];
function keep(el, subArray) {
var found = subArray.filter(function(current) {
return current.id != el.id && current.name == el.name && current.group == el.group;
});
if (found && found.length > 0) {
var keepThis = true
var date1 = new Date(el.date_modified)
found.forEach(function(foundEl) {
var date2 = new Date(foundEl.date_modified)
keepThis = date1.getTime() > date2.getTime() && el.id < foundEl.id
})
return keepThis
} else {
return true
}
}
var filteredArray = array.filter(function(el, index, arr) {
return keep(el, arr)
})
In this snippet if you replace array variable with the fetched data (i.e.: $scope.lists in your controller) you will have the desired output. Keep in mind though that I did not change the id property since this is something that you usually do not change. So my output is:
[
{
"id": 1,
"group": "AB",
"name": "John",
"date_modified": "2018-01-30T12:01:47Z"
},
{
"id": 2,
"group": "BC",
"name": "Alex",
"date_modified": "2018-01-30T12:04:43Z"
},
{
"id": 4,
"group": "CD",
"name": "Peter",
"date_modified": "2018-01-29T12:00:07Z"
}
]

Categories

Resources