This question already has answers here:
Modifying a copy of a JavaScript object is causing the original object to change
(13 answers)
Closed 3 months ago.
Sorry for the strange title, but I've come across an issue that is plain weird. To give some background, i'm working on a booking system that takes a time range as an input from admin, generates available times based on it, and then reduces the available times based on already made bookings (i.e admin specifies availability from 10:00 to 12:00, booking has been made to 11:30, available times will be times = [10:00, 10:30, 11:00, 12:00]).
I have an object that contains per month for each day the available times.
availableTimesPerDay: {
1: ["10:00","10:30","11:00","11:30","12:00"],
2: ["10:00","10:30","11:00","11:30","12:00"],
3: ["10:00","10:30","11:00","11:30","12:00"],
....
}
Where the number represents the date for the given month.
Bookings are represented as an array of objects, format is:
bookedTimes = [
{
date: "2022-12-01T11:30:00.000+02:00"
}
];
I planned to have a function which would iterate through each booking and remove the availability for that time on a given date (based on example above, 11:30 would need to be removed from availableTimesPerDay[1] leaving the value for it as ["10:00","10:30","11:00","12:00"]
The function itself is defined as such:
function reduceAvailableTimesBasedOnDateTime(availableTimesPerDay,bookedTimes){
console.log(JSON.stringify(availableTimesPerDay));
bookedTimes.forEach((bookedDateObject) => {
let bookedDate = new Date(bookedDateObject.date);
// 1
let currentAvailableTimesOnDate = availableTimesPerDay[bookedDate.getDate()];
// ["10:00","10:30","11:00","11:30","12:00"]
let bookedTime = bookedDate.toLocaleTimeString('et');
// "13:30:00"
let time = bookedTime.substring(0,bookedTime.length - 3);
// "13:30"
let index = currentAvailableTimesOnDate.indexOf(time);
// 3
if (index > -1) {
currentAvailableTimesOnDate.splice(index, 1);
// ["10:00","10:30","11:00","12:00"]
}
})
console.log(JSON.stringify(availableTimesPerDay));
return availableTimesPerDay;
}
The way I understand this function is that i've extracted a specific array of available times into a new variable and removed a specific time from that array. I have done no modifications on an original data and I would expect at this stage the availableTimesPerDay to remain unmodified. However, when I run my code, the availableTimesPerDay is modified even though I do no operations with availableTimesPerDay object itself.
What's even stranger is that the modification is not just strictly done on the 1st element, but on all specific dates that have the same day of the week. Here's output from the console for the console.log(availableTimesPerDay) defined in the function (note that 11:30 value is removed on dates 1st of December, 8th of December, 15th of December etc.
booking-helper.js:94 {"1":["10:00","10:30","11:00","11:30","12:00"],"2":[],"3":[],"4":[],"5":[],"6":[],"7":[],"8":["10:00","10:30","11:00","11:30","12:00"],"9":[],"10":[],"11":[],"12":[],"13":[],"14":[],"15":["10:00","10:30","11:00","11:30","12:00"],"16":[],"17":[],"18":[],"19":[],"20":[],"21":[],"22":["10:00","10:30","11:00","11:30","12:00"],"23":[],"24":[],"25":[],"26":[],"27":[],"28":[],"29":["10:00","10:30","11:00","11:30","12:00"],"30":[],"31":[]}
booking-helper.js:105 {"1":["10:00","10:30","11:00","12:00"],"2":[],"3":[],"4":[],"5":[],"6":[],"7":[],"8":["10:00","10:30","11:00","12:00"],"9":[],"10":[],"11":[],"12":[],"13":[],"14":[],"15":["10:00","10:30","11:00","12:00"],"16":[],"17":[],"18":[],"19":[],"20":[],"21":[],"22":["10:00","10:30","11:00","12:00"],"23":[],"24":[],"25":[],"26":[],"27":[],"28":[],"29":["10:00","10:30","11:00","12:00"],"30":[],"31":[
What's even more interesting is that if I copy the same function to codepen with same data or call it directly from the browsers console it works as expected - it removes the specific time from a specific date.
The way I understand this function is that I've extracted a specific array of available times into a new variable and removed a specific time from that array. I have done no modifications on an original data and I would expect at this stage the availableTimesPerDay to remain unmodified.
But that's not what is happening. A mere assignment of an array to a new variable does not create a new array. The new variable will reference the same array. So whatever mutation you bring to that array will be visible whether you look at that array via currentAvailableTimesOnDate or via availableTimesPerDay[bookedDate.getDate()]: they are just different ways to see the same array object.
If you don't want that splice to affect availableTimesPerDay[bookedDate.getDate()], then you must take a copy:
let currentAvailableTimesOnDate = [...availableTimesPerDay[bookedDate.getDate()]];
What's even stranger is that the modification is not just strictly done on the 1st element, but on all specific dates that have the same day of the week.
This would suggest that you have initialise availableTimesPerDay with a similar misunderstanding, so that all entries in that array reference the same array. This could for instance happen when you had initialised it as follows:
let availableTimesPerDay = Array(7).fill( ["10:00","10:30","11:00","11:30","12:00"]);
This creates one array ["10:00","10:30","11:00","11:30","12:00"] and populates the outer array with duplicate references to that array.
You should solve that too, and do something like this:
let availableTimesPerDay = Array.from({length: 7}, () =>
["10:00","10:30","11:00","11:30","12:00"]
);
Now that array literal is evaluated 7 times, each time producing a new array.
It seems like you might be under the mistaken assumption that this code:
let currentAvailableTimesOnDate = availableTimesPerDay[bookedDate.getDate()];
makes a copy of the array and you are then operating on the copy, not the original array. But that's not the case. You're essentially just aliasing the same array and then operating on it. To demonstrate:
const availableTimesPerDay = {
1: ["10:00","10:30","11:00","11:30","12:00"],
2: ["10:00","10:30","11:00","11:30","12:00"],
3: ["10:00","10:30","11:00","11:30","12:00"],
};
const currentAvailableTimesOnDate = availableTimesPerDay[1];
currentAvailableTimesOnDate.splice(0, 100);
console.log(availableTimesPerDay[1]);
If you run this code in the browser console, it will log an empty array, even though you "do no operations with availableTimesPerDay object itself."
To copy the array, you have at least a few options:
const currentAvailableTimesOnDate = availableTimesPerDay[1].slice();
// OR
const currentAvailableTimesOnDate = [...availableTimesPerDay[1]];
// OR
const currentAvailableTimesOnDate = Array.from(availableTimesPerDay[1]);
Using any of the above code, you would then be operating on a copy of the array, not the original one.
Regarding the day-of-week thing, that sounds to me like you are using getDay() instead of getDate() somewhere, though I do not see that in your code, and in fact you say you do not see that in the browser console. I don't have a clear answer for that but could it be that at one point you had getDay() and you are accidentally running an older version of the code that is different from what you are showing here and testing in the console?
I am using cloudant database to store my values and I specify my
document ID to be string+date(ddmmyy) (e.g foo071117). Recently I
need to filter the documents to show documents in the last 7 days
(e.g foo311017 to foo061117) and I have used views to do this. This is my code:
function (doc) {
var datearr = [];
var today = new Date()
for (var i =1; i<=7; i++){
var datesfull = new Date(today.getFullYear(), today.getMonth(), today.getDate() - i);
var dd = datesfull.getDate();
if (dd<10){
dd = '0'+dd;
}
var mm = datesfull.getMonth()+1;
if (mm<10){
mm ='0'+mm
}
var yy = datesfull.getFullYear().toString().substr(-2);
var dates = "foo" + dd + mm + yy;
datearr.push(dates);
}
if((datearr.indexOf(doc._id.slice(0,-1))>=0) || (datearr.indexOf(doc._id)>=0)){
emit(doc._id,doc);
}
}
The slice is for when my json files gets too big and I have to split the file into multiple files (e.g foo0711170, foo0711171)
This code works at the beginning as after running, cloudant view only showed 7 documents which I wanted. However, as the days passes, the old files remains instead of having just 7 documents. Is Cloudant supposed to refresh to show 7 documents in the View as specified in my code or is this due to how the data is stored?
*I can't query data for the past 7 days using URL as I am using powerBI which limits my ability to write a code to specify which document ID to extract so I can only use views as a filter
When you supply a design document to Cloudant with an index definition, the view is built and stored on disk. You may then query the view at a later time to get its results.
In your design document your are getting the time of "now", which in this case is the time when indexing is performed - not the time that the query is performed. It therefore cannot be used to extract data "in the last 7 days" (of the query time).
One solution would be to change the way your document _id is built. If you used: yyyymmdd+string, then your documents would be stored in date order because Cloudant has a primary index on the _id field.
You could then query the database's primary index, starting with a key 7 days in the past e.g.:
/mydb/_all_docs?startkey=20171101&include_docs=true
I have dataset as
{"id":1,"Timestamp":"Mon, 11 May 2015 07:57:46 GMT","brand":"a"}
{"id":2,"Timestamp":"Mon, 11 May 2015 08:57:46 GMT","brand":"a"}
The expected result of data is
{"id":1,"Timestamp":ISODate("2015-05-11T07:57:46Z"),"brand":"a"}
{"id":2,"Timestamp":ISODate("2015-05-11T08:57:46Z"),"brand":"b"}
It means I want to revise the Timestamp in each row from string to ISODate
My current code is
db.tmpAll.find().forEach(
function (a) {
a.Timestamp = new Date(a.Timestamp);
db.tmpAll2.insert(a);
}
);
It runs sucessfully, but it will take couple minutes to run the code and it need to create a new collection. Is there any efficient way to do it?
You don't need to create new collection. Use the collection.save method to update your document.
db.tmpAll.find().forEach(function(doc){
doc.Timestamp = new Date(doc.Timestamp);
db.tmpAll.save(doc);
})
This question already has answers here:
Array.push() makes all elements the same when pushing an object [duplicate]
(2 answers)
Dates changing when I change another date [duplicate]
(1 answer)
Closed 26 days ago.
I'm trying to create an array of date objects starting from a certain date up till today.
Here's the code I have:
var beginning = new Date("04,06,2013");
var dates = [];
var today = new Date();
while (beginning < today){
var x = beginning;
console.log(x);
dates.push(x);
beginning.setDate(beginning.getDate()+1)
}
for (var i in dates) {
console.log(dates[i]);
}
In the while loop I see the correct dates incrementing but when I print out the dates in the array at the last for loop I see all the dates that are pushed being today's date.
Any ideas?
What your code does is push a whole bunch of references to the exact same Date object. So, you have an array full of all the same Date object and each time you change that object, all elements in the array just point to the same object so they will all appear to change.
When you push an object into an array or assign an object to a variable, it does not make a copy, it pushes a reference to it (think of it like a pointer in other languages). To push different date objects for each iteration of the loop, you'd have to create a new date object each time through the loop and push that.
In javascript, assigning an object or an array to any variable (which includes pushing it into an array) only assigns a reference to that object or array, not a copy. This is a common issue that bits most people coming up to speed on javascript.
You can make a new date object each time through the loop like this:
var beginning = new Date("04,06,2013");
var dates = [];
var today = new Date(), x;
while (beginning < today){
x = new Date(beginning.getTime());
console.log(x);
dates.push(x);
beginning.setDate(beginning.getDate()+1)
}
You're only working with one single Date instance throughout all that code.
To create a copy of a Date, do this:
x = new Date(beginning.getTime());
Then call the .setDate() method to move it forward.
The setters on JavaScript Date instances change the object. They don't create a new one.
EDIT 4/16/2012: I solved the issue of getting the timezone abbreviated into a letter format, had to download a third party sorting method and add a few things to get the desired results. The only problem now is Daylight Savings Time handlers, but there are a bunch of subjects on that. However if anyone knows how to handle UTC Daylight Savings hanlers, please feel free to help.
Thank you everyone.
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
I've made an html table that I've binded with a javscript viewmodel using knockoutjs that pulls the info from a private server usin JSON function. I'm trying to make each column sortable (click on the column header once to get everything in descending order according to that column's info; click header again to get everything in ascending, and a third time to get everything in it's original order).
PLEASE NOTE: I have searched for my problem and have seen other solutions, but nothing so far has worked for me. I'm hoping to find a solution specific towards my code.
The Javascript ViewModel.js file is basically like this:
Event(column1, column2, ...., columnN){
var self = this;
self.column1 = column1;
self.column2 = column2;
.
.
}
//Sort column2 that has the Dates (dd (day) HHMM (hours/minutes) mmm (month) yy (year) format)
self.sortColumn = "Column2"
self.sortAscending = true;
self.SortByDates = function(){
if(self.sortColumn == "Column2")
self.sortAscending = !self.sortAscending;
else{
self.sortColumn = "Column2"
self.sortAscending = true;
}
self.rows.sort(function(a,b){
if(self.sortAscending == true)
for(self.Column2 in self.rows)
return a.Column2 > b.Column2 ? 1 : a.Column2 < b.Column2 ? -1 : 0;
else
return a.Column2 < b.Column2 ? 1 : a.Column2 > b.Column2 ? -1 : 0;
});
}
//specify location of server and info and get them
function getEvents(){
$.getJSON("http://.........",
function (data){
$.each(data.d, function(i, item){
handleEvent(item)
})
}
);
}
//pushes (AKA populates) info from server into the table
function handleEvent(item){
var newEvent = new Event(item.Column1InfoFromServer,
formatJSONDate(item.Column2DateInfoFromServer), .....)
this.Model.rows.push(newEvent);
}
//Formats the date info from server into dd (day) HHMM (hours/minutes) mmm (month) yy (year)
formatJSONDate(jsonDate){
var date = new Date(parseInt(jsonDate.substr(6)));
return date.format("dd HHMM mmm yy");
}
this.Model = new ViewModel();
this.getEvents();
ko.applyBindings(this.Model);
I'm having one hell of a hard time getting the Date in its converted form (yes it HAS to be in that form --> actually, I still need to figure out how to include the time-zone abbreviation right after the 'HHMM' part based off of UTC). So lets say I have "11 1136 Apr 12" and "22 1624 Jan 12" among other dates in the table. Right now when I try sorting the table according to the dates, they don't sort appropriately. Any help is appreciated, thank you.
EDIT: To be clear, I'm trying to display the timezones in military timezone codes (timezones 'A'-'Z'). Also, the dates being taken from the server are already in UTC.
UPDATE:
I was looking at another question, and someone created a knockout grid addon:
https://github.com/ericmbarnard/KoGrid
I bet this might help you out :-)
---OLD ANSWER for nostalgia----
There are some great helper functions in the Underscore library, one of them being sort:
http://documentcloud.github.com/underscore/#sortBy
sortBy_.sortBy(list, iterator, [context]) Returns a sorted copy of
list, ranked in ascending order by the results of running each value
through iterator. Iterator may also be the string name of the property
to sort by (eg. length).
_.sortBy([1, 2, 3, 4, 5, 6], function(num){ return Math.sin(num); });
=> [5, 4, 6, 3, 1, 2]
I'd give this a shot, along with creating a better model for your dates. It sounds like you need to store a property which is a unique point in time, along with a text value for the user.
Well, to get numeric values for your date objects, sort by pDate.valueOf(). This will give you the # of millisecond since epoch.
However, there is an issue inside of your sort function, but I'm not sure what it is supposed to do. You can't walk an object inside of a sort function and return values like that.