Sort an Array of MovieClips on values within their child clips - actionscript-3

I have a class called Table that is linked to a clip on the stage.
During setup I create many Table instances and pass each one an instance of a TableData class. This TableData object includes an ID value.
I put a selection of my Table objects in an Array which I would lik eto sort based on the ID value within each Table's instance of TableData.
What I had hoped to use was something like: myArray.sortOn("tableData.id");
This doesn't seem to work and I assume that Array.sortOn() can't drill down into child clips.
Is there a way that I can achieve this?

The Array and generic Vector.<> collections both provide an alternate form of the .sort() method which takes a function. You can provide your own sorting implementation, in this case which peeks objects and compares only specific pieces of them.
Consider the following class:
class TableData {
public var ID:String;
public function TableData(id:String):void {
this.ID = id;
}
}
This is a simplified version of your TableData class that only provides a ID. Then a simplified Table class:
class Table {
private var _data:TableData;
public function get data():TableData {
return _data;
}
public function Table(tableData:TableData) {
_data = tableData;
}
}
Finally, lets put them to use, and spin up a new collection.
var list:Vector.<Table> = new Vector.<Table>();
list.push(new Table(new TableData("X")));
list.push(new Table(new TableData("C")));
list.push(new Table(new TableData("S")));
list.push(new Table(new TableData("A")));
Now, we need to make a new function that will actually do our comparison:
private function sortUsingDataID(a:Table, b:Table):int {
var aID:String = a.data.ID;
var bID:String = b.data.ID;
return aID < bID ? aID == bID ? 0 : -1 : 1;
}
This function will expect to get two items, and should return either -1, 0, or 1. If a should come before b then the function should return -1. Otherwise, if a should come after b, then it should return 1. Lastly, if the two are the same, then it should return a value of 0.
So, now we just need to tell the array to resort itself using our custom sorting function by calling .sort and passing in our function.
list.sort(sortUsingDataID);
Now the list should be sorted the way that you want.
See an working example here.
See the documentation for more details

Related

Scala Saving Sort Function Parameters

I'm trying to save the parameters used to sort a sequence in Scala for deferred execution at a later time.
For example, instead of "list.sortBy (.value)", I want to save the (".value") sort function, and retrieve this sort function ("_.value") at a later time for the actual sorting.
How do I save and retrieve the sort function arguments for deferred execution? Here is some sample test code:
class SortTest {
def testSort () = {
val myClass = new MyClass(0)
val list = List (myClass, new MyClass(1), new MyClass(2), new MyClass(3), new MyClass(4))
// Want to sort by value attribute, but don't want to sort right away. Rather
// how do I save the sort function, and retrieve it at a later time for execution?
list.sortBy(_.value)
// save the sort function (i.e. sort by the value attribute of myClass)
// something similar to the following syntax
myClass.setSortFunction (_.value)
// retrieve the sort function and sort the list
list.sortBy(myClass.getSortFunction())
}
class MyClass (d:Int){
val value = d
val sortFunc = null
// what should be the signature of this function ?
def setSortFunction (sortFunc: ()) = {
this.sortFunc = sortFunc
}
// what should be the return type of this function?
def getSortFunction () = {
return sortFunc
}
}
}
You could do something like this:
val sortFunction = (x : { def value: Int } ) => x.value
At this point, you might not be happy with the hardcoding of Int. Unfortunately, a function must have well defined types, so I cannot make this generic on the return type.
One could instead make it a definition:
def sortFunction[T] = (x : { def value: T } ) => x.value
However, you cannot pass definitions around, only values, and values cannot be parameterized.
On the other hand, you are approaching this the wrong way -- there's an assumption there that sortBy takes a function as a parameter, and only that. Not true: sortBy takes two parameters: a function, and an Ordering. If you don't save the ordering, you cannot sort it.
And here we get to the other problem... the function must have a type MyClass => T, and the ordering must be of type Ordering[T]. Without knowing in advance what T is, you cannot save that.
Fortunately, and the reason why Ordering is a good idea, you can simply create an Ordering[MyClass], and use that!
Here's how:
class MyClass(d: Int) {
val value = d
private var sortFunction: Ordering[MyClass] = _
def setSortFunction[T : Ordering](f: MyClass => T) {
sortFunction = Ordering by f
}
def getSortFunction = sortFunction
}
And you use it like this:
list.sorted(myClass.getSortFunction)
Notice that instead of sortBy it uses sorted. The method sortBy is implemented by creating an Ordering and calling sorted with it, so you are not losing any performance.

Trouble understanding how to use table models and observables together

I have some fairly complicated data and at the moment resides in hash tables that point to other classes that also contain hash tables because there are one-many relationships within the data I am trying to preserve.
On the other side of the world, I have a table that works quite nicely, with the exception that it does not have any connection to my data model. It has it's own table model. I create it in a very simple manner-
Object[][] tableData = new Object[4][ tableHeaders.length ];
modelTablet = new TabletTableModel(tableData, tableHeaders );
my tablet data model is very simple,
class TabletTableModel extends DefaultTableModel {
public TabletTableModel(Object rowData[][], Object columnNames[]) {
super(rowData, columnNames);
}
#Override
public Class getColumnClass(int col) {
if (col == 0) {
return String.class;
} else {
return Double.class;
}
}
#Override
public boolean isCellEditable(int row, int col)
{
if (col == 0 || col == activeColumn)
return true;
else
return false;
}
}
which suites me, since I also have to do things like add and remove rows, which with this simple data model, most of the work is done for me.
class AddRowActionListener implements ActionListener {
public void actionPerformed(ActionEvent event) {
DefaultTableModel model = (DefaultTableModel)tableTablet.getModel();
model.addRow(new Object[3]);
System.out.println(Arrays.deepToString(tableData));
}
}
I've also implemented a mouse listener and a cell renderer so that when I click on the header of a column it becomes active and the cells in the adjoining column are repainted in another color to indicate they are no longer editable.
So far so good, but for my interface, I have a second table that summarizes the results of the first table. It's just three rows, no header. I think I may have done more work than I needed to when I made it.
modelSummary = new SummaryTableModel(1, tableHeaders.length);
tableSummary = new JTable(modelSummary);
class SummaryTableModel extends DefaultTableModel {
public SummaryTableModel(int rows, int columns) {
super(rows, columns);
}
#Override
public Class getColumnClass(int col) {
if (col == 0) {
return String.class;
} else {
return Double.class;
}
#Override
public boolean isCellEditable(int row, int col)
{
if (col == 1 && activeColumn == 2)
return true;
else
return false;
}
}
So that gives me a nice set of tables, they recolor like they are supposed to, they limit data to valid values and format it properly (with my renderers I did not list).
But here is what is supposed to happen- the user enters a value in column 1 of table 1, I then need to sum over all the values in column 1, out that value in column 1 of table 2 and then recalculate all the values in column 2 of table 1 based on this new value.
Conversely, if column 2 is the active column and the user changes a value in column 2 of table 1, then I need to get the value of column 1 in table 2 and use it to recalculate all the values for column 1 in table 1 and also sum the values of column 2 and place them in column 2 of table 2. With a caveat that the user can't enter a new value such that the sum of the numbers in column 2 exceeds 100.
Table 2 can also be edited, which would require calculating all the values in table 1.
So... to me that seems like I should have an observable and I should register my tables as both observers and as controllers for the observable. So I can write an observable, but I also still have my two table data models. I have been reading a lot, but all the examples I have found show in an observable used with something simple, like a text field or table models, but not using the Observable class. The other problem is that I am not sure how to do most of the cool things (like adding rows or checking types) that the default model happily does for me by default.
Before I add a whole new dimension of complexity to my project- could someone give me some pointers? Is it easy to mix data models that pretty much have no conception of rows or columns with table data models in some way that doesn't require writing all the default table model operations over again? The whole process seems unbelievably complex to me at the moment. I desparatetly need some clear explanation of how this should work.
Table models are already observable: you may add a TableModelListener to them and thus be called whenever a change occurs in the table model. So, the second model could be a listener for the first one, and vice-versa.
I usually don't use a DefaultTableModel, because it requires me to transform all my domain objects into arrays. I usually just write an AbstractTableModel subclass that wraps a list of domain objects. Getting the value of a cell generally consists in getting the object model at the given row (index) in the list, and then call the appropriate getter on the object, depending on the column.
Setting a value consists in calling the appropriate setter on the object, and firing an event.
See http://docs.oracle.com/javase/tutorial/uiswing/components/table.html#data for a tutorial on table models.

Using Linq to update a List<T> of objects to DB

I have a form that returns me a List of FlatSessie objects
in my edit view I edit a few FlatSessie's and get them returned to my Post method in that List object.
In my DB I have Sessies, which I map using Automapper to FlatSessie's and back
now I can not get linq to make the update to the DB for me.
the code:
[HttpPost]
public ActionResult Sessies(int id, int? modID, int? projID, string schooljaarparam, List<FlatSessie> sl) {
if (ModelState.IsValid) {
foreach (FlatSessie s in sl) { //i run over all FlatSessies which i get
Models.Sessies ses = Mapper.Map<FlatSessie, Sessies>(s); // i map them to the Sessies object
List<Sessies> origReeks = _db.Sessies.Where(p => p.Ses_ID == ses.Ses_ID).ToList(); //i get the original Session by ID. if there is a Session with that ID, if not (the ID will be 0) i do an Insert. if there is i want to do an Update.
if (origReeks.Count > 0) {
//it's an update
UpdateModel(origReeks.First(); //doesnt work
//_db.Sessies.Attach(ses, origReeks.First()); //doesnt work, gives me an error on used ID...
_db.SubmitChanges();
} else {
//no sessies yet, add them, this works.
_db.Sessies.InsertOnSubmit(ses);
_db.SubmitChanges();
}
}
TempData["okmsg"] = "De sessies zijn opgeslagen";
return RedirectToAction("Index");
}
//if not valid, i return the viewdata which i need.
Module m = _db.Modules.First(md => md.Mod_ID == modID.Value);
int antses = m.Mod_AantalSessies.Value;
List<List<SelectListItem>> lpllst = new List<List<SelectListItem>>(antses);
for (int i = 0; i < antses; i++) {
lpllst.Add(MvcApplication.lesplaatsList(schooljaarparam, -1));
}
ViewData["lesplist"] = lpllst;
ViewData["lglist"] = MvcApplication.lesgeverList();
return View(sl);
}
It might work to provide a prefix to UpdateModel (FlatSessie[n], where n is such that it matches the actual input name of the model element in question) so that it knows which properties to map onto the object, but because you are getting a list of these it might not. Have you tried updating the retrieved model using the data from the matching FlatSessie object directly?
Also, once you get this to work, you might want to do a single SubmitChanges for all inserts/updates (outside the loop) so that you get the entire submit wrapped in a single transaction. This will make it easier if there are errors to correct them and resubmit -- since you won't have some changes already committed causing further potential errors.

LINQ variable to list of string without using column names?

In an C# ASP.Net MVC project, I'm trying to make a List<string> from a LINQ variable.
Now this might be a pretty basic thing, but I just cannot get that to work without using the actual column names for the data in that variable. The thing is that in the interests of trying to make the program as dynamic as possible, I'm leaving it up to a stored procedure to get the data out. There can be any amount of any which way named columns depending on where the data is fetched from. All I care about is taking all of their values into a List<string>, so that I can compare user-input values with them in program.
Pointing to the columns by their names in the code means I'd have to make dozens of overloaded methods that all just basically do the same thing. Below is false non-functioning code. But it should open up the idea of what I mean.
// call for stored procedure
var courses = db.spFetchCourseInformation().ToList();
// if the data fails a check on a single row, it will not pass the check
bool passed = true;
foreach (var i in courses)
{
// each row should be cast into a list of string, which can then be validated
// on a row-by-row basis
List courseRow = new List();
courseRow = courses[i]; // yes, obviously this is wrong syntax
int matches = 0;
foreach (string k in courseRow)
{
if (validator.checkMatch(courseRow[k].ToString()))
{
matches++;
}
}
if (matches == 0)
{
passed = false;
break;
}
}
Now below is an example of how I currently have to do it because I need to use the names for the columns
for (int i = 0; i < courses.Count; i++)
{
int matches = 0;
if (validator.checkMatch(courses[i].Name))
matches++;
if (validator.checkMatch(courses[i].RandomOtherColumn))
matches++;
if (validator.checkMatch(courses[i].RandomThirdColumn))
matches++;
if (validator.checkMatch(courses[i].RandomFourthColumn))
matches++;
/* etc...
* etc...
* you get the point
* and one of these for each and every possible variation from the stored procedure, NOT good practice
* */
Thanks for help!
I'm not 100% sure what problem you are trying to solve (matching user data to a particular record in the DB?), but I'm pretty sure you're going about this in slightly the wrong fashion by putting the data in a List. I
t should be possible to get your user input in an IDictionary with the key being used for the column name, and the object as the input data field.
Then when you get the data from the SP, you can get the data back in a DataReader (a la http://msmvps.com/blogs/deborahk/archive/2009/07/09/dal-access-a-datareader-using-a-stored-procedure.aspx).
DataReaders are indexed on column name, so if you run through the keys in the input data IDictionary, you can check the DataReader to see if it has matching data.
using (SqlDataReader reader = Dac.ExecuteDataReader("CustomerRetrieveAll", null))
{
while (reader.Read())
{
foreach(var key in userInputDictionary.AllKeys)
{
var data = reader[key];
if (data != userInputDictionary[key]) continue;
}
}
}
Still not sure about the problem you are solving but, I hope this helps!
A little creative reflection should do the trick.
var courses = db.spFetchCourseInformation()
var values = courses.SelectMany(c => c.GetType().GetProperties() // gets the properties for your object
.Select(property => property.GetValue(c, null))); // gets the value of each property
List<string> stringValues = new List<string>(
values.Select(v => v == null ? string.Empty : v.ToString()) // some of those values will likely be null
.Distinct()); // remove duplicates

Alias properties in LINQ to SQL

I'm using a shim property to make sure that the date is always UTC. This in itself is pretty simple but now I want to query on the data. I don't want to expose the underlying property, instead I want queries to use the shim property. What I'm having trouble with is mapping the shim property. For example:
public partial class Activity
{
public DateTime Started
{
// Started_ is defined in the DBML file
get{ return Started_.ToUniversalTime(); }
set{ Started_ = value.ToUniversalTime(); }
}
}
var activities = from a in Repository.Of<Activity>()
where a.Started > DateTime.UtcNow.AddHours( - 3 )
select a;
Attempting to execute the query results in an exception:
System.NotSupportedException: The member 'Activity.Started' has no supported
translation to SQL.
This makes sense - how could LINQ to SQL know how to treat the Started property - it's not a column or association? But, I was looking for something like a ColumnAliasAttribute that tells SQL to treat properties of Started as Started_ (with underscore).
Is there a way to help LINQ to SQL translate the expression tree to the Started property can be used just like the Started_ property?
There's a code sample showing how to do that (i.e. use client-side properties in queries) on Damien Guard's blog:
http://damieng.com/blog/2009/06/24/client-side-properties-and-any-remote-linq-provider
That said, I don't think DateTime.ToUniversalTime will translate to SQL anyway so you may need to write some db-side logic for UTC translations anyway. In that case, it may be easier to expose the UTC date/time as a calculated column db-side and include in your L2S classes.
E.g.:
create table utc_test (utc_test_id int not null identity,
local_time datetime not null,
utc_offset_minutes int not null,
utc_time as dateadd(minute, 0-utc_offset_minutes, local_time),
constraint pk_utc_test primary key (utc_test_id));
insert into utc_test (local_time, utc_offset_minutes) values ('2009-09-10 09:34', 420);
insert into utc_test (local_time, utc_offset_minutes) values ('2009-09-09 22:34', -240);
select * from utc_test
Based on #KrstoferA's answer I came up with a reliable solution that hides the fact that the properties are aliased from client code. Since I'm using the repository pattern returning an IQueryable[T] for specific tables, I can simply wrap the IQueryable[T] result provided by the underlying data context and then translate the expression before the underlying provider compiles it.
Here's the code:
public class TranslationQueryWrapper<T> : IQueryable<T>
{
private readonly IQueryable<T> _source;
public TranslationQueryWrapper( IQueryable<T> source )
{
if( source == null ) throw new ArgumentNullException( "source" );
_source = source;
}
// Basic composition, forwards to wrapped source.
public Expression Expression { get { return _source.Expression; } }
public Type ElementType { get { return _source.ElementType; } }
public IEnumerator<T> GetEnumerator() { return _source.GetEnumerator(); }
IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
// Intercept calls to the provider so we can translate first.
public IQueryProvider Provider
{
get { return new WrappedQueryProvider(_source.Provider); }
}
// Another wrapper around the provider
private class WrappedQueryProvider : IQueryProvider
{
private readonly IQueryProvider _provider;
public WrappedQueryProvider( IQueryProvider provider ) {
_provider = provider;
}
// More composition
public object Execute( Expression expression ) {
return Execute( expression ); }
public TResult Execute<TResult>( Expression expression ) {
return _provider.Execute<TResult>( expression ); }
public IQueryable CreateQuery( Expression expression ) {
return CreateQuery( expression ); }
// Magic happens here
public IQueryable<TElement> CreateQuery<TElement>(
Expression expression )
{
return _provider
.CreateQuery<TElement>(
ExpressiveExtensions.WithTranslations( expression ) );
}
}
}
Another example cannot hurt I guess.
In my Template class, I have a field Seconds that I convert to TimeStamp relatively to UTC time. This statement also has a CASE (a?b:c).
private static readonly CompiledExpression<Template, DateTime> TimeStampExpression =
DefaultTranslationOf<Template>.Property(e => e.TimeStamp).Is(template =>
(template.StartPeriod == (int)StartPeriodEnum.Sliding) ? DateTime.UtcNow.AddSeconds(-template.Seconds ?? 0) :
(template.StartPeriod == (int)StartPeriodEnum.Today) ? DateTime.UtcNow.Date :
(template.StartPeriod == (int)StartPeriodEnum.ThisWeek) ? DateTime.UtcNow.Date.AddDays(-(int)DateTime.UtcNow.DayOfWeek) : // Sunday = 0
(template.StartPeriod == (int)StartPeriodEnum.ThisMonth) ? new DateTime(DateTime.UtcNow.Year, DateTime.UtcNow.Month, 1, 0, 0, 0, DateTimeKind.Utc) :
(template.StartPeriod == (int)StartPeriodEnum.ThisYear) ? new DateTime(DateTime.UtcNow.Year, 1, 1, 0, 0, 0, DateTimeKind.Utc) :
DateTime.UtcNow // no matches
);
public DateTime TimeStamp
{
get { return TimeStampExpression.Evaluate(this); }
}
My query to initialize a history-table based on (Event.TimeStamp >= Template.TimeStamp):
foreach (var vgh in (from template in Templates
from machineGroup in MachineGroups
let q = (from event in Events
join vg in MachineGroupings on event.MachineId equals vg.MachineId
where vg.MachineGroupId == machineGroup.MachineGroupId
where event.TimeStamp >= template.TimeStamp
orderby (template.Highest ? event.Amount : event.EventId) descending
select _makeMachineGroupHistory(event.EventId, template.TemplateId, machineGroup.MachineGroupId))
select q.Take(template.MaxResults)).WithTranslations())
MachineGroupHistories.InsertAllOnSubmit(vgh);
It takes a defined maximum number of events per group-template combination.
Anyway, this trick sped up the query by four times or so.