Excel sorting is stuck when ClosedXML is used - closedxml

I have a xlsx of 14000 rows.
I can sort it by a column in a second using the standard Excel app.
When I try to do the same in ClosedXML, it is stuck. I mean that it is extremely slow (like 10 minutes or even more), but eventually it completes the operation.
So let's say I create a file
XLWorkbook wb = new XLWorkbook();
IXLWorksheet ws = wb.AddWorksheet("my test");
ws.Cell(1, 1).Value = "A";
ws.Cell(1, 2).Value = "B";
for (int i = 0; i < 14000; i++)
{
ws.Cell(i + 2, 1).Value = i;
ws.Cell(i + 2, 2).Value = i % 2;
}
wb.SaveAs("test.xlsx");
Now there are several ways to sort it by the column B, for example
var tab = ws.Range(1,1,14001,2);
Stopwatch stopwatch = new Stopwatch();
Console.WriteLine("start");
stopwatch.Start();
tab.SetAutoFilter();
wb.SaveAs("test.xlsx");
ws.AutoFilter.Sort(2);
stopwatch.Stop();
Console.WriteLine("Sorted after " + (int)stopwatch.Elapsed.TotalSeconds + " seconds");
wb.SaveAs("test.xlsx");
Should I switch to Microsoft.Office.Interop.Excel to sort a table by a column? Or is there a usable method in ClosedXML?

Yes, sure, the Interop is much better for sorting, it is immediate in this case while ClosedXML is not usable.
Here is the sample code
var excelApp = new Microsoft.Office.Interop.Excel.Application();
// Make the object visible.
excelApp.Visible = true;
excelApp.Workbooks.Open(Path.Combine( Directory.GetCurrentDirectory(), "test.xlsx"));
Worksheet ws = (Worksheet)excelApp.ActiveSheet;
Make sure to use ClosedXML before the above to create the spreadsheet (it is faster than interop in that case).
Finally the sorting part
var tab = (Range)ws.Range[ws.Cells[2, 1], ws.Cells[14001, 2]];
Stopwatch stopwatch = new Stopwatch();
Console.WriteLine("start");
stopwatch.Start();
ws.Cells[1, 2].AutoFilter();
tab.Select();
tab.Sort(ws.Cells[2,2], XlSortOrder.xlAscending);
Console.WriteLine("Sorted after " + (int)stopwatch.Elapsed.TotalSeconds + " seconds");
excelApp.ActiveWorkbook.Save();
excelApp.ActiveWorkbook.Close();
excelApp.Quit();
It will take a second, as it is supposed to.
Sorting helper library shared

Related

How to get a time as a variable in Flash CS6

I am trying to get a time that is in a text document to become a variable in a Flash CS6 AS3 project. I can't seem to find where the problem is and the error messages aren't really helping. The highlighted parts are the changed lines.
Here is the newest code:
this.onEnterFrame = function()
{
var StartTime:URLLoader = new URLLoader();
StartTime.dataFormat=URLLoaderDataFormat.VARIABLES;
StartTime.addEventListener(Event.COMPLETE, onLoaded);
function onLoaded(e:Event):void {
}
StartTime.load(new URLRequest("ResponseTime.txt"));
var today:Date = new Date();
var currentTime = today.getTime();
var targetDate:Date = new Date();
var timeLeft = e.data - currentTime;
var sec = Math.floor(timeLeft/1000);
var min = Math.floor(sec/60);
sec = String(sec % 60);
if(sec.length < 2){
sec = "0" + sec;
}
min = String(min % 60);
if(min.length < 2){
min = "0" + min;
}
if(timeLeft > 0 ){
var counter:String = min + ":" + sec;
time_txt.text = counter;
}else{
var newTime:String = "00:00";
time_txt.text = newTime;
delete (this.onEnterFrame);
}
}
Newest Error:
1120: Access of undefined property e. (Line 17).
First of all, this does nothing :
var StartTime();
It's not correct AS3 code.
Then, AS3 loaders being asynchronous, you must way for the loader to finish load so you can get your variable. I mean that all your code after StartTime.load(...) must be inside the function onLoaded.
This way, when the loader finish loading, you'll have you variable.
That say, URLVariable is NOT a loader. It is an object you can use to put your variable into, and feed them to a loader.
If you want to download some file, use URLLoader (with URLRequest). On this page, there is a good example on how you can do that (skip the part about the dataFormat, though). The date you're requesting will be available in the data property of the event, eg :
var timeLeft = e.data - currentTime;
I'm not asking where currentTime is from, since it's out of the scope of that question.
Good luck.

Appaling RazorEngine 3.3 performance, compared to StringTemplate4

Is there any reason or anything I am doing wrong, why RazorEngine is so slow just to parse 100 different templates? I was looking into StringTemplate, and performed two tests to compare, as per below.
[Test]
public void TestStringTemplate()
{
CsStopwatch stopwatch = new CsStopwatch();
stopwatch.Start();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 100; i++)
{
string template = #"Hello there my name is <Name> <Surname> " + i;
TextParseTests.TestModel model = new TextParseTests.TestModel();
model.Name = "Karl";
model.Surname = "Cassar";
Template t = new Template(template);
t.Add("Name", model.Name);
t.Add("Surname", model.Surname);
var result = t.Render();
sb.AppendLine(result);
}
stopwatch.Stop();
var ms = stopwatch.ElapsedMilliseconds;
int k = 5;
//109ms
}
[Test]
public void TestRazorEngine()
{
CsStopwatch stopwatch = new CsStopwatch();
stopwatch.Start();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 100; i++)
{
string template = #"Hello there my name is #Model.Name #Model.Surname " + i;
TextParseTests.TestModel model = new TextParseTests.TestModel();
model.Name = "Karl";
model.Surname = "Cassar";
var result = Razor.Parse(template, model);
sb.AppendLine(result);
}
stopwatch.Stop();
var ms = stopwatch.ElapsedMilliseconds;
int k = 5;
//24000~ ms
}
The difference is staggering.
StringTemplatev4: 109ms
RazorEngine 3.3: 24,131ms
That is over 200x slower than StringTemplate! I have a lot of content using the RazorEngine format, and I prefer the syntax for RazorEngine way over StringTemplate. However, this is extremely, extremely slow.
Any ideas if I may be doing anything wrong? Please note that I am using different templates on purpose, as if I use caching for RazorEngine it is way faster (down to 300 - 400ms) but my website has a lot of tiny text which are different, and this is the most 'real-life' test I could do.
Razor.Parse will compile the template every time.
You shouldn't do this. Your application should compile the template once, or whenever the template is changed if the template is being modified whilst the application is running.
Razor.Compile(template, name);
Following this your application should use
Razor.Run(name, model);
Compare the times against Razor.Run not Razor.Parse

Reference Shared Object AS3

I am trying to reference a shared object (one that saves data), but whenever I try to, I get a crash.
This code works fine:
var var1:Object = { value:1 };
var varRef:Object = var1;
if(var1.value == 1) {
varRef.value = 50;
}
trace(varRef.value); // outputs 50;
trace(var1.value); // outputs 50;
But when I try to use shared objects, it doesn't work.
import flash.net.SharedObject;
var iapso:SharedObject = SharedObject.getLocal("purchases");
var varRef:Object = iapso.data.testing;
varRef = 90
trace ("The shared value is " + iapso.data.testing);
trace ("This should mirror it" + varRef);
If you're able to figure out the issue, please post a fixed version.
Thanks.
You must come from a programming background where pointers can be dereferenced.
In this case, varRef is not varRef = &iapso. Setting its value does not change the value of iapso.data.testing;
Initially, you set varRef as a pointer to iapso.data.testing:
var varRef:Object = iapso.data.testing;
Then, you immediately change it to a constant value object literal 90:
varRef = 90;
This does not set the value of testing to 90 - this changes the value of varRef.
You could set varRef to iapso.data, then set testing as in:
var varRef:Object = iapso.data;
varRef.testing = 90;
Then, the following would produced expected results:
trace(iapso.data.testing); // 90
trace(varRef.testing); // 90
setting varRef will not update the value in iapso.data - numbers are copied, not referenced.
var bar:Object = { data:1 };
var foo:Object = bar.data;
trace(foo); //"1"
bar.data = 2;
trace(foo); //"1"
If you really want to use a reference, use data instead, eg:
import flash.net.SharedObject;
var iapso:SharedObject = SharedObject.getLocal("purchases");
var varRef:Object = iapso.data;
varRef.testing = 90
trace ("The shared value is " + iapso.data.testing);
trace ("This should mirror it" + varRef.testing);

display lines in AS3

I am baffled by this function, which is called prior to this with parameters 22 and 58 for xVal ad yVal respectively. It doesn't display anything when the swf is compiled and tested, and it's error free. The code is in the document class:
private function mLine(xVal : int, yVal : int) {
var rCol = 0x0000FF;
var incr = Math.round((Math.random() * 20) + 8);
lns.push(new Shape());
var i = lns.length - 1;
this.addChild(lns[i]);
lns[i].graphics.moveTo(xVal, yVal);
lns[i].graphics.lineStyle(10, rCol);
lns[i].graphics.lineTo(xVal, yVal + 20);
lns[i].name = incr;
trace("lns[" + i + "] x is " + lns[i].x); // outputs 'lns[0] x is 0'
trace("xVal is " + xVal); // outputs 'xVal is 22'
trace("yVal is " + yVal); //outputs 'yVal is 58'
trace(stage.contains(lns[i])); // outputs 'true'
}
Assuming you have declared private var lns = []; somewhere, it draws a blue line (20px straight down from the given position).
It doesn't display anything
That means you probably don't have an object of that class on the stage. In your document class, you should use addChild to display an instance of the class containing mLine. mLine needs to be called somehow obviously. You could do this in the class' constructor, but you'd need to remove the last trace statement to avoid a null pointer error, because stage would be null then.
Edit: Missed that you said it is in the Document class. So, try and see if drawing anything else works. The problem doesn't seem to be with this function.
Your code seems like it should work. I have rewrote it to conform better to ActionScript 3 best practices
private function drawLine(xVal:int, yVal:int):void
{
var lineColor:uint = 0x0000FF;
var lineShape:Shape = new Shape();
//lineShape.name = String(Math.round((Math.random() * 20) + 8));
lineShape.graphics.lineStyle(10, lineColor);
lineShape.graphics.moveTo(xVal, yVal);
lineShape.graphics.lineTo(xVal, yVal + 20);
addChild(lineShape);
lines.push(lineShape);
}
The x and y properties of your shape will both be zero because you never set them. you are just drawing lines inside the shape at the xVal and yVal. You could do the same thing like this:
private function mLine(xVal:int, yVal:int)
{
var lineColor:uint = 0x0000FF;
var lineShape:Shape = new Shape();
//lineShape.name = String(Math.round((Math.random() * 20) + 8));
lineShape.graphics.lineStyle(10, lineColor);
lineShape.graphics.moveTo(0, 0);
lineShape.graphics.lineTo(0, 20);
lineShape.x = xVal;
lineShape.y = yVal;
addChild(lineShape);
lines.push(lineShape);
}
Not sure why its not showing up at all for you though.

Flex AdvancedDataGrid c/p rows issue

I'm a newbie in Flex/AS3 development and I came across an issue that bugs me for a while now. I'm using an AdvancedDataGrid with some columns, and an ArrayCollection as the provider. I would like to make a copy/paste functionality so that multiple rows can be selected, copied, and then pasted below the selected (or last selected row).
The problem is when I copy the data from one row to another, both of those rows become highlighted on mouse-over (upper instance isn't even selectable) - just as in this topic: Flex DataGrid/DataProvider bug?
First I thought it was the issue of copying the reference, but it persist even if I use ObjectUtil.copy() method. Furthermore, I manually change one of the properties called "order" so that the objects of the ArrayCollection aren't identical, but it doesn't help. Dataprovider is called newTreatmentData, and the DataGrid is newTreatmentDG.
Any suggestions are more then welcome.
Here's part of the code that is relevant:
private function getSelectedRow(event:Event):void
{
selectedRow = newTreatmentDG.selectedIndex;
}
private function copySelection(event:Event):void
{
bufferData = new ArrayCollection();
var sortedIndices:Array = newTreatmentDG.selectedIndices.sort();
for (var i:int = 0; i < newTreatmentDG.selectedIndices.length; i++){ //copy selected rows to the buffer
var j:int = sortedIndices[i];
bufferData.addItem(newTreatmentData[j]);
}
}
private function pasteSelection(event:Event):void
{
var rowsToMove:int = newTreatmentData.length - selectedRow - 1; //number of rows to move after pasting
for (var i:int = 1; i <= bufferData.length; i++){
if (selectedRow + bufferData.length + i > newTreatmentData.length){ // adding objects to the array collection to avoid range error
newTreatmentData.addItem(null);
}
}
for (i = 1; i <= rowsToMove; i++){
newTreatmentData[selectedRow + bufferData.length + i] = ObjectUtil.copy(newTreatmentData[selectedRow + i]) //first move the rows to "give room" for pasting
newTreatmentData[selectedRow + bufferData.length + i].order = selectedRow + bufferData.length + i; //manually changing the "order" property, but it doesn't help
}
for (var j:int = 1; j <= bufferData.length; j++){ //paste the data from the buffer
newTreatmentData[selectedRow + j] = ObjectUtil.copy(bufferData[j-1])
newTreatmentData[selectedRow + j].order = selectedRow + j; //again changing the order property
}
newTreatmentData.refresh();
}
I solved it by changing the mx_internal_uid property of every object in the dataprovider ArrayCollection. It seems that AdvancedDataGrid checks it to see if rows are equal. I assumed (and you know what they say about assumptions) that an object's UID changes when you copy its value into another object (hence the U in UID ;) ).