re-organize json data with vanilla javascript - javascript

I am consuming data from a web service that sends JSON data with the following structure. You can see my at https://es6console.com/jkadxuk1/.
Problems: my current attempt is very slow. And it works only if all dates have the same number of reporting times. For e.g. in the records below, there are two times files for 2018-08-01 and one for 2018-07-31 but the 31st still shows two records when re-organized. Hope I am making sense.
function dbDateTimeToDateTme(value){
/*
value isforamtted as 2018-08-04T22:00:00Z
Here, we split them
#output: an array of date and time. Note we don't validate
*/
reply=[];
if(value){
date=value.substring(0,10)
time=value.substring(11,19)
reply.push(date,time);
}
return reply;
}
function viewFullTablar(acquired_data){
total=acquired_data.length;
if(total==0){
return false;
}
data=[]; //reorganized data
location_name="";
location_code=0;
datetime=[];
for(var i=0;i<total;i++){
date_index=-1;
place_index=-1; //
location_name=acquired_data[i]['store']
location_code=acquired_data[i]['location_id']
datetime=dbDateTimeToDateTme(acquired_data[i]['for_date']); //0=date,1=time
//now check if we have already added the location by its location_code
for(var counter=0;counter<data.length;counter++){
if (data[counter]['location_id']==location_code){
place_index=counter;
break;
}
}
//do we add the place?
if(place_index==-1){
//yes add it
data.push(
{
'index':i,
'store':location_name,
'location_id':location_code,
'dates':[]
}
);
place_index=0; //first element now
}
//does the date exist yet or not?
date_blocks=data[place_index]['dates'];
for(counter=0;counter<date_blocks.length;counter++){
if (date_blocks[counter]['date']==datetime[0]){
date_index=counter;
break;
}
}
//add the date to the place or not?
if(date_index==-1){
data[place_index]['dates'].push(
{
'date':datetime[0],
'main':[]
}
);
date_index=0;
}
//now add time and weather details
data[place_index]['dates'][date_index]['main'].push(
{
'time':datetime[1],
'income':acquired_data[i]['income']
}
);
}
return data;
}
var data={
"data": [
{
"expense": "1026.2100",
"income": "869.4500",
"location_id": 1,
"for_date": "2018-07-31T04:00:00Z",
"store": "Eastern Province"
},
{
"expense": "1026.3300",
"income": "869.0300",
"location_id": 1,
"for_date": "2018-08-01T00:00:00Z",
"store": "Eastern Province"
},
{
"expense": "1026.7600",
"income": "870.2000",
"location_id": 1,
"for_date": "2018-08-01T04:00:00Z",
"store": "Eastern Province",
},
]
}
console.log(viewFullTablar(data['data']));
Each day can have a maximum 8 different hours of reporting but the minimum can be as low as 1 or 0 even (if nothing has been filed yet).
For display purposes, I want to get the following array out of if:
[{
store:"Eastern Province",
location_id:1,
dates:[
{
'date':'2018-07-31',
main:[
{'time':04:00:00,
'income':1026
}];
}];
}];
or:
East Province
2018-07-31
04:00:00 value value
09:00:00 value value
2018-08-01
09:00:00 value value
10:00:10 value value
I have added https://es6console.com/jkadxuk1/ here.

You can use reduce to greatly improve readability. Use .find() to look back through the array to find objects that already exist, and update them accordingly:
const data = {
"data": [
{
"expense": "1026.2100",
"income": "869.4500",
"location_id": 1,
"for_date": "2018-07-31T04:00:00Z",
"store": "Eastern Province"
},
{
"expense": "1026.3300",
"income": "869.0300",
"location_id": 1,
"for_date": "2018-08-01T00:00:00Z",
"store": "Eastern Province"
},
{
"expense": "1026.7600",
"income": "870.2000",
"location_id": 1,
"for_date": "2018-08-01T04:00:00Z",
"store": "Eastern Province",
},
]
};
const res = data.data.reduce((obj, item) => {
// Do we already have this location in the final array?
const foundItem = obj.find((i) => i.location_id == item.location_id);
const date = item.for_date.substr(0,10);
const time = item.for_date.substr(11,8);
// Let's just append the date
if (foundItem) {
// Search for the current date
const foundDate = foundItem.dates.find((i) => i.date == date);
// Is there already a date in the array?
if (foundDate) {
// Push the time into the array
foundDate.main.push({
time,
expense: item.expense,
income: item.income
});
} else {
// Push a new date object, with this time
foundItem.dates.push({
date,
main: [{
time,
expense: item.expense,
income: item.income
}]
});
}
} else {
// Push a whole new location
obj.push({
store: item.store,
location_id: item.location_id,
dates: [
{
date,
main: [{
time,
expense: item.expense,
income: item.income
}]
}
]
});
}
return obj;
}, []);
console.log(res);

You can use reduce using id and date as key value if you don't want to do a lot of iterations, after that you can use map or other function to unwind result
const data = [
{
"expense": "1026.2100",
"income": "869.4500",
"location_id": 1,
"for_date": "2018-07-31T04:00:00Z",
"store": "Eastern Province"
},
{
"expense": "1026.3300",
"income": "869.0300",
"location_id": 1,
"for_date": "2018-08-01T00:00:00Z",
"store": "Eastern Province"
},
{
"expense": "1026.7600",
"income": "870.2000",
"location_id": 1,
"for_date": "2018-08-01T04:00:00Z",
"store": "Eastern Province",
},
];
const result = data.reduce((acum = {}, current) => {
const year = current.for_date.slice(0, 10);
const hour = current.for_date.slice(11, 19);
if (!acum[current.location_id]) {
acum[current.location_id] = {
store: current.store,
location_id: current.location_id,
dates: {
[current.for_date]: {
date: current.for_date,
main: [
{
time: '',
income: current.income,
expense: current.expense,
},
],
}
}
}
} else if (!acum[current.location_id].dates[year]) {
acum[current.location_id].dates[year] = {
date: year,
main: [
{
time: '',
income: current.income,
expense: current.expense,
},
],
}
} else {
acum[current.location_id].dates[year].main.push({
time: '',
income: current.income,
expense: current.expense,
});
}
return acum;
}, {});
console.log(result);
console.log('---------')
let arr = Object.keys(result).map(key => {
let res = result[key]
res.dates = Object.keys(result[key].dates).map(date => result[key].dates[date])
return res;
});
console.log(arr)

Related

How to sort a collection of reservations

Model looks like so:
{
time: String,
date: Date,
user_id: String
},
return of end point get user reservations are like so
"reservations": [
{
"_id": "63e3bda0a2b3be512b44ba51",
"time": "10:40",
"date": "2023-02-06T00:00:00.000Z",
"user_id": "63d4657708205fda2de700f1"
},
{
"_id": "63e3bda2a2b3be512b44ba56",
"time": "11:00",
"date": "2023-02-06T00:00:00.000Z",
"user_id": "63d4657708205fda2de700f1"
},
{
"_id": "63e3c22bf83f5954d210eb56",
"time": "13:00",
"date": "2023-02-06T00:00:00.000Z",
"user_id": "63d4657708205fda2de700f1"
},
{
"_id": "63e3c224f83f5954d210eb48",
"time": "10:00",
"date": "2023-02-10T00:00:00.000Z",
"user_id": "63d4657708205fda2de700f1"
},
{
"_id": "63e3c228f83f5954d210eb4f",
"time": "10:00",
"date": "2023-02-16T00:00:00.000Z",
"user_id": "63d4657708205fda2de700f1"
}
]
How do i sort these reservations based on date and then time. So the earliest date with the earliest time slot, and this done inn all dates.
Can this be achieved with mongoose?
What i have tried:
const dates = reservations.map((res) => res.date.toISOString());
function onlyUnique(value: any, index: any, self: string | any[]) {
return self.indexOf(value) === index;
}
var distinctDates = dates.filter(onlyUnique);
First get all unique dates
for (let i = 0; i < distinctDates.length; i++) {
const date = new Date(distinctDates[i]);
let distinctReservations: { _id: any; time: number; date: Date; user_id: string }[] = [];
for (let j = 0; j < reservations.length; j++) {
const reservation = reservations[j];
if (reservation.date.toISOString() === date.toISOString()) {
distinctReservations.push({
_id: reservation.id,
time: hhMMToMinutes(reservation.time),
date: reservation.date,
user_id: reservation.user_id
});
}
}
const sortedByMinutes = distinctReservations.sort((a, b) => a.time - b.time);
sortedReservationsSlotToMinutes.push(sortedByMinutes);
}
const flated = sortedReservationsSlotToMinutes.flat(2);
const sortedReservations = flated.map((res) => {
let timeSlot = {
hour: (res.time / 60).toFixed(),
minute: ''
};
const minuteModule = res.time % 60;
if (minuteModule) {
timeSlot.minute = minuteModule.toString();
} else {
timeSlot.minute = '00';
}
return {
_id: res._id,
time: timeSlot.hour + ':' + timeSlot.minute,
date: res.date,
user_id: res.user_id
};
});
return sortedReservations;
Then loop through all unique dates and find matching of reservations. Reservations is a param inn this function. Then convert their time to minutes, sort based on the smalest value, then push those reservations into an array. After that i then push all reservations sorted by minutes to an outer array then i flaten that array and return it.
Then i convert back the minutes to string.
You can construct a datetime using $dateFromParts and use the datetime for sorting.
db.reservations.aggregate([
{
"$addFields": {
"dateTime": {
"$dateFromParts": {
"year": {
$year: "$date"
},
"month": {
$month: "$date"
},
"day": {
$year: "$date"
},
"hour": {
$toInt: {
"$substrCP": [
"$time",
0,
2
]
}
},
"minute": {
$toInt: {
"$substrCP": [
"$time",
3,
2
]
}
}
}
}
}
},
{
$sort: {
dateTime: 1
}
}
])
Mongo Playground

Remove a sub level of an array

I get a list of items with add-ons from the server, but when I try to delete an add-on from this list I can't. I noticed that when I try to access the property grupoAdicionais.produto.codigo, it does not exist because it has a sublevel coming from the API, how do I remove this to have access to my product.codigo?
Array received from API:
"grupoAdicionais":[
{"produto": {"codigo":21, "descricao":"Bacon"}, "item":148657, "quantidade":1, "total":5},
{"produto": {"codigo":13193, "descricao":"Queijo"}, "item":148657, "quantidade":1, "total":1}
]
My code in the reducer to return the list without the extra:
REMOVER_ADICIONAL: (state, action) => {
let itemRemover = action.item;
let listaReducer = state.lstItensRestauranteQRcode;
const itemRemovido = listaReducer.filter((item) => {
return item.grupoAdicionais.produto.codigo != itemRemover.produto.codigo;
});
state.lstItensRestauranteQRcode = itemRemovido;
},
If all you want to do is get a list of the codes:
const response = {"grupoAdicionais": [{
"produto": {
"codigo": 21,
"descricao": "Bacon"
},
"item": 148657,
"quantidade": 1,
"total": 5
}, {
"produto": {
"codigo": 13193,
"descricao": "Queijo"
},
"item": 148657,
"quantidade": 1,
"total": 1
}]}
const codigos = response.grupoAdicionais.map(grupo => grupo.produto.codigo)
console.log(codigos)
// =>
[ 21, 13193 ]
I'm not totally sure, but it seems like maybe you want to remove a group by its code.
const removeByCode = (code) => response.grupoAdicionais.filter((group) => group.produto.codigo !== code)
const newGroups = removeByCode(21)
console.log(newGroups)
// =>
[
{
produto: { codigo: 13193, descricao: 'Queijo' },
item: 148657,
quantidade: 1,
total: 1
}
]
var response = {"grupoAdicionais": [{
"produto": {
"codigo": 21,
"descricao": "Bacon"
},
"item": 148657,
"quantidade": 1,
"total": 5
}, {
"produto": {
"codigo": 13193,
"descricao": "Queijo"
},
"item": 148657,
"quantidade": 1,
"total": 1
}]}
console.dir(response.grupoAdicionais[0].produto.codigo)
grupoAdicionais is an array here, you have to access it like this:
console.dir(response.grupoAdicionais[0].produto.codigo)

Filter on two arrays same time?

I have two arrays:
const array1 = [{
"id": "4521",
"name": "Tiruchirapalli",
"stateId": "101"
},
{
"id": "1850",
"name": "Tenkasi",
"stateId": "101"
},
{
"id": "202",
"name": "Thanjavur",
"stateId": "101"
},
{
"id": "505",
"name": "Ernakulam",
"stateId": "102"
},
];
And now array2
const array2 = [{
"id": 1850,
"cityName": "Tenkasi",
"aliasNames": [
"Thenkasi"
]
},
{
"id": 4521,
"cityName": "Tiruchirapalli",
"aliasNames": [
"Trichy"
]
},
{
"id": 202,
"cityName": "Thanjavur",
"aliasNames": [
"Tanjore"
]
},
{
"id": 505,
"cityName": "Ernakulam",
"aliasNames": [
"Kochi",
"Cochin"
]
},
];
what i need to do is, how to filter both the arrays at same time ( or filter first one and then second which ever one is performance effective ).
For instance, when user types "Kochi", first it should check on array1 to find if its has name="Kochi", if it has then we can set the state with that and if it doesnt have we need to find it on array2 and the update the state !
Which is fast and effective way to handle this - ( array1 has 2500 records and array2 has 990 records ) so performance / speed is also a concern
My attempt:
searchFilterFunction = text => {
this.setState({ typedText: text });
const newData = array1.filter(item => {
const itemData = `${item.name.toUpperCase()}`;
const textData = text.toUpperCase();
return itemData.indexOf(textData) > -1;
});
this.setState({ data: newData});
};
How to implement the second filter in optimized way ?
For instance, when user types "Kochi", first it should check on array1
to find if its has name="Kochi", if it has then we can set the state
with that and if it doesnt have we need to find it on array2 and the
update the state !
I would do something like this with Array.find.
if( array1.find(item=>item.name.toUpperCase() === text) ) {
// set state
} else if( array2.find(item=>item.cityName.toUpperCase() === text) ) {
// set state
}
A refined form would be
let result = array1.find(item=>item.name.toUpperCase() === text);
// check in array 2 as we cannot find in array 1
if(!result) {
result = array2.find(item=>{
// check in aliasNames and in cityName
return item.cityName.toUpperCase() === text || item.aliasNames.includes(text);
}
);
}
if(result) {
setState(result);
} else {
// place not found
}
Regarding the performance based on your array count you will not see much difference. If you want to save some milliseconds you can check the array with least count first as mentioned in one of the comments. But the time also varies based on were the element is in array.
I think this is the most optimal solution because nesting the two filter won't work as you need to filter from first array and then second.
const array1 = [{
"id": "4521",
"name": "Tiruchirapalli",
"stateId": "101"
},
{
"id": "1850",
"name": "Tenkasi",
"stateId": "101"
},
{
"id": "202",
"name": "Thanjavur",
"stateId": "101"
},
{
"id": "505",
"name": "Ernakulam",
"stateId": "102"
},
];
const array2 = [{ "id": 1850, "cityName": "Tenkasi",
"aliasNames": [
"Thenkasi"
]
},{"id": 4521,"cityName": "Tiruchirapalli",
"aliasNames": [
"Trichy"
]
},
{
"id": 202,
"cityName": "Thanjavur",
"aliasNames": [
"Tanjore"
]
},
{
"id": 505,
"cityName": "Ernakulam",
"aliasNames": [
"Kochi",
"Cochin"
]
},
];
function filter(text) {
// Complexity Linear
const filter_array = array1.filter((a) => {
return (a.name === text)
});
if (filter_array.length > 0) {
//Set State and return
}
//Complexity Linear and includes complexity Linear O(sq(m*n)) where n is //the aliasName record
const filter_array2 = array2.filter((a) => {
return a.cityName === text || a.aliasNames.includes(text);
});
return filter_array2 //Set State filter array 2
}
console.log(filter("Kochi"));

Is there a way to count distinct values in mongoose with specific cut off date

For example, I have the following two docs
[
{
"_id": "5fc534505144dd0030c44f8e",
"createdAt": "2020-12-14T15:11:21.327Z"
"user_id": "2",
},
{
"_id": "5fc534505144dd0030c44f8e",
"createdAt": "2020-12-14T14:10:40.427Z",
"user_id": "1"
},
{
"_id": "5fc534595144dd0030c44f95",
"createdAt": "2020-12-13T14:10:58.027Z",
"user_id": "1"
}
]
the results should be
[
{
"date": "2020-12-13",
"count":1
},
{
"date": "2020-12-14",
"count":2
}
]
where the count is the number of distinct docs via user_ids till the date that specific cut off date
given data
data=[
{
"createdAt": "2020-12-14T15:11:21.327Z",
"user_id": "2",
},
{
"createdAt": "2020-12-14T14:10:40.427Z",
"user_id": "1"
},
{
"createdAt": "2020-12-13T14:10:58.027Z",
"user_id": "1"
},{
"createdAt": new Date("2020-12-14T14:10:58.027Z"),
}
]
> db.dummy.insert(data)
You may use aggregate: use $group with _id being the date's day in conjunction with $sum)
> db.dummy.aggregate({$group:{_id:{$substr:['$createdAt', 0, 10]}, count:{$sum:1}}})
{ "_id" : "2020-12-14", "count" : 3 }
{ "_id" : "2020-12-13", "count" : 1 }
edit: mongoose wise same may hold
const mongoose = require('mongoose')
mongoose.connect('mongodb://localhost:27017/dummy')
const UDate = mongoose.model('X', { createdAt:String, user_id: String }, 'dummy')
;(async()=>{
mongoose.set('debug', true)
const group = {$group:{_id:{$substr:['$createdAt', 0, 10]}, count:{$sum:1}}}
const v = await UDate.aggregate([group])
console.log('s : ', JSON.stringify(v))
mongoose.disconnect()
})()
edit2: to handle unicity of userIds so there are not counted twice per date, you may use $addToSet instead of sum followed by a projection using $size
const group = {$group:{_id:{$substr:['$createdAt', 0, 10]}, userIds:{$addToSet:'$user_id'}}}
const count = {$project:{date:'$_id', count: {$size: '$userIds'}} }
const v = await Stock.aggregate([group, count])
Lastly, if you feel always more, you can "rename" the _id field as date during the projection
{$project:{date:'$_id', _id:0, count: {$size: '$userIds'}} }
$gorup by createdAt date after getting substring using $substr and make array of unique user ids on the base of $addToset
get total element in count array using $size
db.collection.aggregate([
{
$group: {
_id: { $substr: ["$createdAt", 0, 10] },
count: { $addToSet: "$user_id" }
}
},
{ $addFields: { count: { $size: "$count" } } }
])
Playground

Create JSON with data from database and JavaScript generated

I need to create a calendar view with fullcalendar.io. For some dates, I have a specific price in my database and I retrieve it, but for some dates (without specific prices) I need to put the usual rates in the objects I need to create with JavaScript. Problem is now because I don't know how to make JSON for that.
In short: I need to have a price for every date, but for some dates I get data from database. How do I create such JSON objects in JavaScript?
I have this code:
var db_data = [
{
"id": 5,
"user_id": 1,
"article_id": 5,
"title": "",
"start": "2016-03-25 15:18:46"
},
{
"id": 4,
"user_id": 1,
"article_id": 5,
"price": 55,
"title": "",
"start": "2016-03-15 15:18:46"
},
{
"id": 3,
"user_id": 1,
"article_id": 5,
"price": 35,
"title": "",
"start": "2016-03-07 15:18:46"
},
{
"id": 2,
"user_id": 1,
"article_id": 5,
"price": 22,
"title": "drugi",
"start": "2016-03-05 15:18:46"
},
{
"id": 1,
"user_id": 1,
"article_id": 5,
"price": 44,
"title": "prvi",
"start": "2016-02-04 15:18:46"
}
];
// declare variables
var period_start = new Date('2016-02-02'),
period_end = new Date('2016-03-03'),
current_date = period_start,
array_of_all_dates = [];
// Create a populated array of dates
// Create a populated array of dates
while (current_date.getTime() <= period_end.getTime()) {
array_of_all_dates.push(current_date);
current_date = new Date(+current_date);
current_date.setDate(current_date.getDate() + 1);
}
// Now loop over the array of populated dates and mutate, so something like
array_of_all_dates = array_of_all_dates.map(function (date) {
var found_in_db = db_data.filter(function (db_data) {
return new Date(db_data.start.replace(" ", "T")).getTime() === date.getTime(); // You need to do this comparison better!
});
if (found_in_db.length > 0) {
return found_in_db[0];
}
var new_object = {
title: '',
start: date,
price: '{{$article->price}}'
};
console.log(new_object);
return new_object;
});
console.log('result'+array_of_all_dates);
drawCalendar(array_of_all_dates);
And with this code I get data from database and dates (start) which are not excist in database I create with JavaScript.
But with this function I get this data and I can't create calendar:
I also try with this:
// Now loop over the array of populated dates and mutate, so something like
array_of_all_dates = array_of_all_dates.map(function (date) {
var found_in_db = db_data.filter(function (db_data) {
var db_data_date = new Date(db_data.start.replace(" ", "T"));
return db_data_date.getFullYear() === date.getFullYear() &&
db_data_date.getMonth() === date.getMonth() &&
db_data_date.getDay() === date.getDay();
});
if (found_in_db.length > 0) {
return found_in_db[0];
}
var new_object = {
a_property: 'some_default_value',
start: date
};
console.log(new_object);
return new_object;
});
But currently I get this:
I don't see how this:
new Date(db_data.start.replace(" ", "T")).getTime() === date.getTime()
can ever be true. The dates in db_data have a time set in them "2016-03-15 15:18:46", but the dates you create in array_of_all_dates do not Date('2016-02-02').
Your second date comparison seems to work, but I am unclear what it is you hope to be the result of the:
array_of_all_dates.map( ... );
In some case you return an element from db_data which looks like this:
{ "id": 5", "user_id": 1, "article_id": 5, "title": "", "start": "2016-03-25 15:18:46" }
and if there was no "match" you return an object that looks like this:
{ a_property: 'some_default_value', start: date }
Note that all the original elements of array_of_all_dates are replaced by this operation.
What is it that you want to end up in array_of_all_dates so you can pass it to drawCalendar?

Categories

Resources