I have an ArrayCollection(neList) of Objects(neObj). Each neObj has several fields like ipAddress,TID,etc.. In most cases neObj will be have values of both TID and ipAddress, rarely it will not have TID but have ipAddress... After adding Objects(neObj), I need to sort the ArrayCollection whose behaviour must be similar to array.sort() which has got strings only..(i.e nos first followed by strings in alphabetical order)
Things I have tried:
1)Using neList.source.sort() and neList.refresh.. but it did not work as neList.source has objects not straight forward things like strings
2)I think i cannot use sortOn function of ArrayCollection as it can be done on only 1 field
My Requirement:
Use Case1:- Objects in ArrayCollection have both TID and IP
neObj1.TID="RAPC" neObj1.ipAddress="121.1.1.2"; neObj2.TID="RAPA" neObj2.ipAddress="121.1.1.1"
O/P after sorting should be
neObj2 neObj1
Use Case2:- 1 of the objects does not have TID
neObj1.ipAddress="121.1.1.2"; neObj2.TID="RAPA" neObj2.ipAddress="121.1.1.1"
O/P after sorting should be
neObj1 neObj2
As hinted in the comments, you'll need to use a sort compareFunction to decide how the items will be sorted.
I do like to point out that sorting a combination of letters and numbers is tricky in the sense that there is no natural order by default. e.g. when sorting, 1, 2 and 11, the order will be 1, 11, 2. You can however solve this using the naturalCompare method in the AS3Commons Lang project.
Here's a code sample for your case. The sort is implemented as a subclass of the Sort class so that you can easily reuse it in other collections:
package {
import mx.collections.Sort;
import org.as3commons.lang.StringUtils;
public class NaturalSort extends Sort {
public function NaturalSort() {
compareFunction = function (a:Object, b:Object, fields:Array = null):int {
var stringA:String = (("TID" in a) ? a.TID : "AAAA") + a.ipAddress;
var stringB:String = (("TID" in b) ? b.TID : "AAAA") + b.ipAddress;
return StringUtils.naturalCompare(stringA, stringB);
};
}
}
}
To apply this:
var collection:ArrayCollection;
collection.sort = new NaturalSort();
collection.refresh();
Related
is there any solution? e.g. I have data in Map with key favorites_ prefix and values _suffix (for example: favorites_jeans, favorites_suit,...,). I want to by dint of loop get that values and set in List, because of it I must give keys of map, right?
I want to know how can I get values of myMap["favorites_*"] (* - after the favorites_ any symbols).
List<String> favoritesStrings = ['favorite_name','favorite_jeans',];
Map<String,dynamic> myMap = {
favoritesStrings[0]:'0',
favoritesStrings[1]:'1',
'someKey':'2',
'anotherKey':'3',
};
favoritesStrings.forEach((favorite)=>print(myMap[favorite]));//prints 0 1
As per what I understood, you want to fetch value from map using "favorites_" + a dynamic value from list as key.
You just have to use String templates and use $ to insert suffix variable to build key dynamically:
List<String> suffixList = ["jeans", "suit", "shirt"];
for(String suffix in suffixList) {
var item = myMap["favorites_$suffix"];
// Do something with item
}
Hope it helps
I have 2 List one stores the name of filterable columns(of type DropDown) and another store the values to load in those filterable columns.
List<string> filterableFields = new List<string>() { "A_B", "C_D", "E_F" };
List<string> AB, CD , EF;
Now at the run time I get the data from web service and I have written a function to to extract values for these filterable fields and store the values to 2nd List.
private void prepareListForFilterableColumns(XDocument records)
{
foreach (var currentField in filterableFields)
{
var values = (from xml in records.Descendants(z + "row")
let val = (string)xml.Attribute("ows_" + currentField.Replace("_", "_x0020_"))
where val != ""
orderby val
select val
).Distinct();
switch (currentField)
{
case "A_B": AB = values.ToList(); break;
case "C_D": CD = values.ToList(); break;
}
}
}
Now I was thinking that instead of hard coding the assignment in swtich case block, If I could just use the first List name "A_B" and replace "_" from it to point to my 2nd List and assign values.ToList() to it.
I understand that c# is a static language, So not sure if we can achieve this, but IF I can it will make my function generic.
Thanks a lot in advance for time and help.
Vishal
You could use a dictionary of lists of strings instead of 3 lists to store the values.
Dictionary<string, List<string>> val lists = new Dictionary<string,List<string>>();
And make the keys of the dictionary equal to the filterables: "AB", "CD",..
then, instead of AB you would use valLists["AB"] and could then reference reach list based on a string key.
The other option would be to use reflection but that would be slower and unnecessarily a bit more complicated.
How do I resolve the error of duplicate variable definitions? There has to be
separate namespaces and use for each definition, but I'm just not seeing it.
CODE
I didn't write this, but I've been trying to unpackage it and change the classes and seem to have broken it. I want to use this for time-scaling the playback of my movies.There's cool math in here for time-scaling.
//time-scaling script
import flash.display.*;
import flash.events.Event.*;
var _time_scale:Number = .25;
var _frames_elapsed:int = 0;
var _clip:MovieClip;
function Main():void {
_clip = new SomeClip;
addEventListener(Event.ENTER_FRAME, handleEnterFrame);
//integer??
function handleEnterFrame(e:Event):void {
_frames_elapsed ++;
}
// we multiply the "real" time with our timescale to get the scaled time
// we also need to make sure we give an integer as a parameter, so we use Math.round() to round the value off
_clip.gotoAndStop(Math.round(_clip.totalFrames * _frames_elapsed * _time_scale ));
}
var myTimer:Timer = new Timer(10);
myTimer.addEventListener(TimerEvent.TIMER, timerListener);
function timerListener (e:TimerEvent):void{
ball1.rotationY += 5;/////////replace function///////////
}
myTimer.start();
ERRORS
**3596**
Warning: Duplicate variable definition.
**1151**
A conflict exists with definition _clip in namespace internal
NOTES
integers, non nested loop
It's because you are missing the ending "}" of the constructor, after this line:
addEventListener(Event.ENTER_FRAME, handleEnterFrame);
And the two following lines should probably be in your constructor, not just in the class declaration:
var myTimer:Timer = new Timer(10);
myTimer.addEventListener(TimerEvent.TIMER, timerListener);
If you are using the Timer and TimerEvent classes, you should import them:
import flash.utils.Timer;
import flash.events.TimerEvent;
Also, you don't need the .* at the end of the Event import.
Another "also". You should have access modifiers on your members ie. vars, and functions. So you should really say:
private var _clip:MovieClip;
It sounds to me like you need to look into the basics of AS3. Here is a really good starting point: http://www.actionscript.org/resources/articles/611/1/Getting-started-with-Actionscript-3/Page1.html
_clip is a reserved key word, you'll have to use something else.
For example I have two ArrayCollection's - firstAC and secondAC. If I do secondAC = firstAC, and than I make changes to secondAC (prehaps put a filterfunction on it) it somehow propagates to firstAC, would anyone tell me why that happens in Flex or Actionscript 3?
What can I do if I only want secondAC to get all data from firstAC but then when I make changes to secondAC it does not show in firstAC?
Thanxs a bunch for answers!
Ladislav
When you write secondAC = firstAC, you simply state that secondAC and firstAC are references to the same array collection.
What you want is to clone the first collection (as in, copy all elements one by one).
You should be able to do it with something like :
secondAC = new ArrayCollection();
secondAC.addAll(firstAC);
I have no idea of Flex or Actionscript, but looks like firstAC and secondAC point to the same array, therefore that's expected.
What you should do is just create another array, copy members, and they will be two real different entities.
Instead of secondAC = firstAC, you can try secondAC.addAll(firstAC).
In ECMAScript languages (AS1-3, JavaScript, et al.), when you use
var foo = //some value which is not a String or a Number
what you are really saying is "foo now points to the same object as that other variable." This means that in this situation, both arrays will be the same value:
var foo:Array = [ 1, 2, 3 ];
foo = bar;
bar.push( 4 );
trace( foo ); // 1, 2, 3, 4
This also works for functions:
var foo:Array = [ 1, 2, 3 ];
adder( foo );
function adder( bar:Array ):void {
bar.push( 4 );
}
trace( foo ); // 1, 2, 3, 4
and it even works with XML:
var xml:XML = <root><foo/></root>;
var bar:XML = xml;
bar.children()[ 0 ].#bar = 1;
trace( xml.toXMLString() ); // <root><foo bar="1"/></root>
This is called "passing by reference" instead of "passing by value" or "passing by copy". It means that every time that an item is referenced, each variable will point to the same object.
There are many ways to get around this, and most of them depend on your context. For arrays, my favorite is Array.concat(), which returns a literal clone of the array. This means that anything I do to the returned value will not effect the original in any way. If I'm dealing with XML, however, I will do something like: var xml2:XML = XML( xml.toXMLString() );.
In your case, I would actually recommend that you use:
var secondAC:ArrayCollection = new ArrayCollection( firstAC.source.concat() );
This has the major benefits of not only being faster (it relies on compiled code instead of Flex SDK code and it also does not first instantiate a new array and then re-populate it), but it also has the distinct benefit of being available in older versions of Flex 3's SDK -- it is entirely backwards compatible.
I am using a Flex dataGrid, and need to sort some of my columns numerically.
Looking at the sortCompareFunction, it seems like i need to create a different function for each column that i want to sort, because my sort function has to know what field it is sorting on.
Is there any way that I can pass the field to be sorted on into the function? so that I only need one numeric sorting function.
I did it using this function:
function fieldNumericSorter(field:String):Function {
return function (obj1:Object, obj2:Object):int {
return sign( int(obj1[field]) - int(obj2[field]) );
}
}
and for each column that needed sorting set
colToBeSorted.sortCompareFunction = fieldNumericSorter("fieldname");
If you are using a DataGrid with an XML dataProvider, this worked for me (modified from Alex's answer):
private function xmlDataGridNumericSorter(field:String):Function
{
return function (obj1:Object, obj2:Object):int
{
var num:Number = ((Number)(obj1.attribute(field)) - (Number)(obj2.attribute(field)));
return (num > 0) ? 1 : ((num < 0) ? -1 : 0);
}
}
and
dataGridColumn.sortCompareFunction = xmlDataGridNumericSorter(xmlAttribute.name().toString());
A very nice solution, considering how common a procedure this probably is..
Thanks Alex, hope this helps people further.
I did not use a numeric function to sort - instead did the following:
arrayCollObject.addItem({Col1: rowData[0], Col2: parseFloat(rowData[1])});
This seems to do the sorting correctly.