JSON Group by Name and then Display Latest Record - javascript

Using only JavaScript, I need to
Group by code.
Get latest modifieddate.
Display total grouped code as Count.
Starting JSON Result
[
{"ID":1,"code":"AAA","modifieddate":"2019-06-01","user":"John"},
{"ID":2,"code":"AAA","modifieddate":"2019-06-02","user":"Jane"},
{"ID":3,"code":"AAA","modifieddate":"2019-06-03","user":"Sue"},
{"ID":4,"code":"BBB","modifieddate":"2019-06-10","user":"Rick"},
{"ID":5,"code":"CCC","modifieddate":"2019-06-11","user":"Joe"}
]
Desired JSON Result set
[
{"ID":3,"code":"AAA","modifieddate":"2019-06-03","user":"Sue","Count":"3"},
{"ID":4,"code":"BBB","modifieddate":"2019-06-10","user":"Rick","Count":"1"},
{"ID":5,"code":"CCC","modifieddate":"2019-06-11","user":"Joe","Count":"1"}
]
Tried using reduce method.
I do not have access to modify the server side API code.
I am using Aurelia JS.

You can use Array.reduce to group the result set by each item's code property, incrementing Count as needed, then take the values from the accumulation object. Along the way, we perform a date comparison to determine which the most recent entry to include in the result.
const data = [ {"ID":1,"code":"AAA","modifieddate":"2019-06-01","user":"John"}, {"ID":2,"code":"AAA","modifieddate":"2019-06-02","user":"Jane"}, {"ID":3,"code":"AAA","modifieddate":"2019-06-03","user":"Sue"}, {"ID":4,"code":"BBB","modifieddate":"2019-06-10","user":"Rick"}, {"ID":5,"code":"CCC","modifieddate":"2019-06-11","user":"Joe"} ];
const result = Object.values(data.reduce((a, e) => {
if (!a[e.code]) {
a[e.code] = {...e, Count: 0};
}
if (Date.parse(e.modifieddate) > Date.parse(a[e.code].modifieddate)) {
a[e.code] = {...e, Count: a[e.code].Count};
}
a[e.code].Count++;
return a;
}, {}));
console.log(result);
By the way, this is just a plain JS array we're working with, not JSON.

This should get you:
let array = [
{"ID":1,"code":"AAA","modifieddate":"2019-06-01","user":"John"},
{"ID":2,"code":"AAA","modifieddate":"2019-06-02","user":"Jane"},
{"ID":3,"code":"AAA","modifieddate":"2019-06-03","user":"Sue"},
{"ID":4,"code":"BBB","modifieddate":"2019-06-10","user":"Rick"},
{"ID":5,"code":"CCC","modifieddate":"2019-06-11","user":"Joe"}
]
let result = array.reduce(function(total, currentValue, currentIndex, arr) {
let index = total.findIndex(function(entry) { return entry.code == currentValue.code; })
if (index >= 0) { // entry already exists
// check modified
if (total[index].modifieddate > currentValue.modifieddate) { // already have most recent of the two
total[index].Count += 1;
} else { // need to replace with more recent
currentValue.Count = total[index].Count + 1;
total[index] = currentValue;
}
} else { // first record for this code
currentValue.Count = 1;
total.push(currentValue);
}
return total;
}, []);
console.log(result);
Here is a working js-fiddle
Note: Comments are made in code block

Related

How to get specific data in JSON result

Here my JSON result of my "modeles" of car:
[
{
"idModele":1,
"modele":"M3",
"marque":{
"idMarque":1,
"marque":"BMW"
}
},
{
"idModele":2,
"modele":"RS6",
"marque":{
"idMarque":2,
"marque":"Audi"
}
},
{
"idModele":3,
"modele":"C63 AMG",
"marque":{
"idMarque":3,
"marque":"Mercedes"
}
},
{
"idModele":4,
"modele":"Clio RS Trophy",
"marque":{
"idMarque":4,
"marque":"Renault"
}
},
{
"idModele":5,
"modele":"Scirocco Type R",
"marque":{
"idMarque":5,
"marque":"Volkswagen"
}
},
{
"idModele":6,
"modele":"118d",
"marque":{
"idMarque":1,
"marque":"BMW"
}
}
]
I just want to get the "modeles" that have the "idMarque:1" (BMW) (in my result they have 2 "modeles") but I don't know how to do it.
My backend : API REST with SpringBoot
My frontend : Angular
Assuming the json array is stored in the variable result, you may simply:
Loop over the json-array.
Check each json-object for the desired condition.
for (let i = 0; i < result.length; i++) {
if (result[i].marque.idMarque === 1) {
console.log('Found it ', result[i]);
}
}
Even simpler:
result.filter(e => e.marque.idMarque === 1);
First, just for clarification, this is a javascript question. It doesn't matter what your backend or frontend is.
Answering your question, you can filter your result to get only the elements you're seeking:
filteredCars = cars.filter(car => car.marque.idMarque === 1)
This will filter the cars with marque.idMarque = 1.
You can find about the filter function on the docs.
You can get the model having idMarque:1 using filter operator. For example you get the JSON result in result class variable. Then you can use filter as follows.
let BMWCars = this.result.filter(e => e.marque.idMarque == 1);
Is a good idea to check, if you have searched object values, so:
let filtered = models
.filter(item => item.marque && item.marque.idMarque
? item.marque.idMarque === 1
: '')
In this case, if you did not get error, when marque key is missing from server response.

Dynamically create array of objects, from array of objects, sorted by an object property

I'm trying to match and group objects, based on a property on each object, and put them in their own array that I can use to sort later for some selection criteria. The sort method isn't an option for me, because I need to sort for 4 different values of the property.
How can I dynamically create separate arrays for the objects who have a matching property?
For example, I can do this if I know that the form.RatingNumber will be 1, 2, 3, or 4:
var ratingNumOne = [],
ratingNumTwo,
ratingNumThree,
ratingNumFour;
forms.forEach(function(form) {
if (form.RatingNumber === 1){
ratingNumOne.push(form);
} else if (form.RatingNumber === 2){
ratingNumTwo.push(form)
} //and so on...
});
The problem is that the form.RatingNumber property could be any number, so hard-coding 1,2,3,4 will not work.
How can I group the forms dynamically, by each RatingNumber?
try to use reduce function, something like this:
forms.reduce((result, form) => {
result[form.RatingNumber] = result[form.RatingNumber] || []
result[form.RatingNumber].push(form)
}
,{})
the result would be object, with each of the keys is the rating number and the values is the forms with this rating number.
that would be dynamic for any count of rating number
You could use an object and take form.RatingNumber as key.
If you have zero based values without gaps, you could use an array instead of an object.
var ratingNumOne = [],
ratingNumTwo = [],
ratingNumThree = [],
ratingNumFour = [],
ratings = { 1: ratingNumOne, 2: ratingNumTwo, 3: ratingNumThree, 4: ratingNumFour };
// usage
ratings[form.RatingNumber].push(form);
try this its a work arround:
forms.forEach(form => {
if (!window['ratingNumber' + form.RatingNumber]) window['ratingNumber' + form.RatingNumber] = [];
window['ratingNumber' + form.RatingNumber].push(form);
});
this will create the variables automaticly. In the end it will look like this:
ratingNumber1 = [form, form, form];
ratingNumber2 = [form, form];
ratingNumber100 = [form];
but to notice ratingNumber3 (for example) could also be undefined.
Just to have it said, your solution makes no sense but this version works at least.
It does not matter what numbers you are getting with RatingNumber, just use it as index. The result will be an object with the RatingNumber as indexes and an array of object that have that RatingNumber as value.
//example input
var forms = [{RatingNumber:5 }, {RatingNumber:6}, {RatingNumber:78}, {RatingNumber:6}];
var results = {};
$.each(forms, function(i, form){
if(!results[form.RatingNumber])
results[form.RatingNumber]=[];
results[form.RatingNumber].push(form);
});
console.log(results);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
HIH
// Example input data
let forms = [{RatingNumber: 1}, {RatingNumber: 4}, {RatingNumber: 2}, {RatingNumber: 1}],
result = [];
forms.forEach(form => {
result[form.RatingNumber]
? result[form.RatingNumber].push(form)
: result[form.RatingNumber] = [form];
});
// Now `result` have all information. Next can do something else..
let getResult = index => {
let res = result[index] || [];
// Write your code here. For example VVVVV
console.log(`Rating ${index}: ${res.length} count`)
console.log(res)
}
getResult(1)
getResult(2)
getResult(3)
getResult(4)
Try to create an object with the "RatingNumber" as property:
rating = {};
forms.forEach(function(form) {
if( !rating[form.RatingNumber] ){
rating[form.RatingNumber] = []
}
rating[form.RatingNumber].push( form )
})

How to sort data and measure the length as per key value using Javascript

I need one help.I have some array of data i need to sort the as per key value and measure the length.I am explaining my code below.
var response = [{
day_id:1,
day_name:"Monday",
subcat_id:"2",
cat_id:"1",
comment:"hii"
}, {
day_id:1,
day_name:"Monday",
subcat_id:"1",
cat_id:"2",
comment:"hello"
}
{
day_id:2,
day_name:"Tuesday",
subcat_id:"3",
cat_id:"2",
comment:"hii"
}]
Here for day_id=1 there are two set of data and same for day_id=2 present.I need to measure length of data set present as per day_id e,g.for day_id=1 length is 2Please help me.
You can map response and save in an object how many times each id was seen:
var counter = {};
response.map(function(item) {
counter[item.day_id] = (counter[item.day_id] || 0) + 1;
});
Your results are in counter:
console.log(counter);
// { '1': 2, '2': 1 }
Hope it helps.
Do not use .map, use .forEach as it doesn't return another array
and the code could be a little cleaner:
var counter = {};
response.forEach(function(item) {
counter[item.day_id] = ++counter[item.day_id] || 1;
});

refactor two $.each() loops to one

I have a JSON object like this...
{
"tasks":[
{
"id":"task_3",
"taskName":"Task A",
"assignee":"Barrack Obama",
"timeReqOptimisitic":"4",
"timeReqNormal":"8",
"timeReqPessimistic":"14",
"timeUnit":"Days",
"timeReq":"8.33",
"positionX":493,
"positionY":101,
"lockStatus":"unlocked"
}
],
"milestones":[
{
"id":"task_1",
"milestoneName":"Start",
"positionX":149,
"positionY":109,
"lockStatus":"unlocked",
"milestoneDate":"2015-04-07"
},
{
"id":"task_2",
"milestoneName":"Finish",
"positionX":989,
"positionY":367,
"lockStatus":"unlocked",
"milestoneDate":"2015-04-22"
}
],
"connections":[
{
"connectionId":"con_10",
"pageSourceId":"task_1",
"pageTargetId":"task_3"
},
{
"connectionId":"con_20",
"pageSourceId":"task_3",
"pageTargetId":"task_2"
}
]
}
...this is a minimal version. In practice, there are numerous items in "tasks", "milestones" and "connections".
I need to iterate through the object and determine the "id" of the "milestones" item with the lowest/earliest "milestoneDate", then identify the "connections" item that has the same value for its "pageSourceId" and return its "pageTargetId".
So in the above example:
Step 1) Iterate through the object and determine the "id" of the "milestones" item with the lowest/earliest "milestoneDate".
Answer: milestones.id = "task_1"
Step 2) Identify the "connections" item that has the same value for its "pageSourceId".
Answer: connections.pageSourceId = "task_1"
Step 3) Return its "pageTargetId".
Answer: "task_3"
I have a working example here. However, I would like to know if there is a way to accomplish this without using the extremely high start date and also in one loop.
As you are not parsing the same array on these two loops, there is no way to merge your loops.
Anyway, you can yet remove the loops to access to the arrays:
http://jsfiddle.net/gael/sruvtwre/2/
$.each(object.milestones, function( index, value ) {
if(startDate > parseDate(value.milestoneDate)) {
startDate = parseDate(value.milestoneDate);
id = value.id
}
});
$.each(object.connections, function( index, value ) {
if(id == value.pageSourceId) {
pageTargetId = value.pageTargetId;
}
});
May be also sorting, and indexing your datas. Then you would need no loops:
Elements in milestones should be sorted, so the earliest milestones element would be milestones[0].
Elements in connections should be indexed by their pageTargetId property, so the requested element should be connections[id].
Your two loops would become:
var pageTargetId= object.connections[ object.milestones[0].id ].pageTargetId;
http://jsfiddle.net/gael/sruvtwre/4/
As said in comments, sorting is not an optimal solution, even if that does not really matter for small sets.
Roughly, there is no no needs to sort all the datas, just the latest matters.
You can use array reduce method, as an comparable alternative to a simple loop:
var latestMilestone= object.milestones.reduce(function(milestone1, milestone2){
if( parseDate(milestone1.milestoneDate) > parseDate(milestone2.milestoneDate) )
return milestone1;
else
return milestone2;
//convert date to timestamp
function parseDate(date) {
var parts = date.split('-');
return Date.UTC(parts[0], parts[1]-1, parts[2]); // Note: months are 0-based
}
});
How about this:
Assuming you get the milestones.id = "task_1" in first loop; outside the loop we can have use jQuery grep. As connections will have unique pageSourceId, grep will return an array with only one object.
var filteredData = jQuery.grep('CONNECTIONS_ARRAY', function(element, index){
return element.pageSourceId == 'MILESTONES_ID'; // Which you get in the loop earlier
});
Then we can access pageTargetId like this:
if(filteredData.length){
filteredData[0].pageTargetId;
}
Try
var dates = []
, ids = []
, filtered = $.map(data.milestones, function(value, index) {
dates.push(new Date(value.milestoneDate).getTime());
ids.push(value.id);
if (dates.length === data.milestones.length) {
var id = ids[$.inArray(Math.min.apply(Math, dates), dates)]
, res = $.grep(data.connections, function(task, key) {
return task.pageSourceId === id
})[0].pageTargetId;
return res
}
})[0]; // `"task_3"`
var data = {
"tasks":[
{
"id":"task_3",
"taskName":"Task A",
"assignee":"Barrack Obama",
"timeReqOptimisitic":"4",
"timeReqNormal":"8",
"timeReqPessimistic":"14",
"timeUnit":"Days",
"timeReq":"8.33",
"positionX":493,
"positionY":101,
"lockStatus":"unlocked"
}
],
"milestones":[
{
"id":"task_1",
"milestoneName":"Start",
"positionX":149,
"positionY":109,
"lockStatus":"unlocked",
"milestoneDate":"2015-04-07"
},
{
"id":"task_2",
"milestoneName":"Finish",
"positionX":989,
"positionY":367,
"lockStatus":"unlocked",
"milestoneDate":"2015-04-22"
}
],
"connections":[
{
"connectionId":"con_10",
"pageSourceId":"task_1",
"pageTargetId":"task_3"
},
{
"connectionId":"con_20",
"pageSourceId":"task_3",
"pageTargetId":"task_2"
}
]
};
var dates = []
, ids = []
, filtered = $.map(data.milestones, function(value, index) {
dates.push(new Date(value.milestoneDate).getTime());
ids.push(value.id);
if (dates.length === data.milestones.length) {
var id = ids[$.inArray(Math.min.apply(Math, dates), dates)]
, res = $.grep(data.connections, function(task, key) {
return task.pageSourceId === id
})[0].pageTargetId;
return res
}
})[0];
document.write(filtered);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
</script>

Reconstruct JSON after duplicates have been removed

I have the following JSON -
{
"node1":[
{
"one":"foo",
"two":"foo",
"three":"foo",
"four":"foo"
},
{
"one":"bar",
"two":"bar",
"three":"bar",
"four":"bar"
},
{
"one":"foo",
"two":"foo",
"three":"foo",
"four":"foo"
}
],
"node2":[
{
"link":"baz",
"link2":"baz"
},
{
"link":"baz",
"link2":"baz"
},
{
"link":"qux",
"link2":"qux"
},
]
};
I have the following javascript that will remove duplicates from the node1 section -
function groupBy(items, propertyName) {
var result = [];
$.each(items, function (index, item) {
if ($.inArray(item[propertyName], result) == -1) {
result.push(item[propertyName]);
}
});
return result;
}
groupBy(catalog.node1, 'one');
However this does not account for dupicates in node2.
The resulting JSON I require is to look like -
{
"node1":[
{
"one":"foo",
"two":"foo",
"three":"foo",
"four":"foo"
},
{
"one":"bar",
"two":"bar",
"three":"bar",
"four":"bar"
}
],
"node2":[
{
"link":"baz",
"link2":"baz"
},
{
"link":"qux",
"link2":"qux"
},
]
};
However I cannot get this to work and groupBy only returns a string with the duplicates removed not a restructured JSON?
You should probably look for some good implementation of a JavaScript set and use that to represent your node objects. The set data structure would ensure that you only keep unique items.
On the other hand, you may try to write your own dedup algorithm. This is one example
function dedup(data, equals){
if(data.length > 1){
return data.reduce(function(set, item){
var alreadyExist = set.some(function(unique){
return equals(unique, item);
});
if(!alreadyExist){
set.push(item)
}
return set;
},[]);
}
return [].concat(data);
}
Unfortunately, the performance of this algorithm is not too good, I think somewhat like O(n^2/2) since I check the set of unique items every time to verify if a given item exists. This won't be a big deal if your structure is really that small. But at any rate, this is where a hash-based or a tree-based algorithm would probably be better.
You can also see that I have abstracted away the definition of what is "equal". So you can provide that in a secondary function. Most likely the use of JSON.stringify is a bad idea because it takes time to serialize an object. If you can write your own customized algorithm to compare key by key that'd be probably better.
So, a naive (not recommended) implementation of equals could be somewhat like the proposed in the other answer:
var equals = function(left, right){
return JSON.stringify(left) === JSON.stringify(right);
};
And then you could simply do:
var res = Object.keys(source).reduce(function(res, key){
res[key] = dedup(source[key], equals);
return res;
},{});
Here is my version:
var obj = {} // JSON object provided in the post.
var result = Object.keys(obj);
var test = result.map(function(o){
obj[o] = obj[o].reduce(function(a,c){
if (!a.some(function(item){
return JSON.stringify(item) === JSON.stringify(c); })){
a.push(c);
}
return a;
},[]); return obj[o]; });
console.log(obj);//outputs the expected result
Using Array.prototype.reduce along with Array.prototype.some I searched for all the items being added into the new array generated into Array.prototype.reduce in the var named a by doing:
a.some(function(item){ return JSON.stringify(item) === JSON.stringify(c); })
Array.prototype.some will loop trough this new array and compare the existing items against the new item c using JSON.stringify.
Try this:
var duplicatedDataArray = [];
var DuplicatedArray = [];
//Avoiding Duplicate in Array Datas
var givenData = {givenDataForDuplication : givenArray};
$.each(givenData.givenDataForDuplication, function (index, value) {
if ($.inArray(value.ItemName, duplicatedDataArray) == -1) {
duplicatedDataArray.push(value.ItemName);
DuplicatedArray.push(value);
}
});

Categories