Combine/merge Dynamic Objects in AS3 - apache-flex

I have 2 dynamic objects and I want to build one to contain all the properties:
var o1:Object = {prop1:val1,prop2:val2,prop3:val3};
var o2:Object = {prop3:val3a,prop4:val4};
and I need to obtain a third object that looks like that:
{prop1:val1, prop2:val2, prop3:val3a, prop4:val4};
Basically I need a way to iterate through the object properties and to add new properties to the third object. I have to mention I'm quite new to AS3/Flash/Flex.

First question, do you really mean to have prop3 in both objects? you will need to decide what to do in case of a collision like that, which object has precedence.
Secondly, check out the introspection apis: http://livedocs.adobe.com/flex/3/html/help.html?content=usingas_8.html
something like this should work:
public function mergeDynamicObjects ( objectA:Object, objectB:Object ) : Object
{
var objectC:Object = new Object();
var p:String;
for (p in objectA) {
objectC[p] = objectA[p];
}
for (p in objectB) {
objectC[p] = objectB[p];
}
return objectC;
}
If the property exists in A and B, B's will overwrite A's. Also note that if the values of a property is an object, it will pass a reference, not a copy of the value. You might need to clone the object in those cases, depending on your needs.
Note: I haven't actually tested the above, but it should be close. Let me know if it doesn't work.
Updated to fix the errors. Glad it works for you though.

You can dynamically access/set properties on objects with the index operator. The for loop will itterate over the property names, so if you put it all together, the following test passes:
[Test]
public function merge_objects():void {
var o1:Object = {prop1:"one", prop2:"two", prop3:"three"};
var o2:Object = {prop3:"threeA", prop4:"four"};
var o3:Object = new Object();
for (var prop in o1) o3[prop] = o1[prop];
for (var prop in o2) o3[prop] = o2[prop];
assertThat(o3.prop1, equalTo("one"));
assertThat(o3.prop2, equalTo("two"));
assertThat(o3.prop3, equalTo("threeA"));
assertThat(o3.prop4, equalTo("four"));
}

you can iterate over the object properties like:
var obj1:Object = new Object();
for(var str:String in obj2){
obj1[str] = "any value"; // insert the property from obj2 to obj1
}

Related

Using Reflection to Change Properties in Swift

I am attempting to create a serializer that will change the properties of an object.
Example:
class testobj{
var prop1:Int = 3
var prop2:String = "Hello"
var prop3:Dictionary<String,String> = Dictionary<String,String>()
}
I know I can access the names and types of the properties using
reflect(testobjc())[0].1
and
var tester = testobj()
_std_lib_DemangledTypeName(tester.prop1)
but what I would like to do is something like
var tester = testobj()
for(var x:Int = 0; x < reflect(testobj()).count; x++){
if(_std_lib_DemangledTypeName(tester.(reflect(testobj())[0].1)) == "Swift.String"){
tester.(reflect(testobj())[0].1) = "World!"
}
}
Essentially, I want to loop through all the properties listed for a given class and set the properties on a newly created object of that class. Any guidance would be appreciated. Swift reflection is new to me.
You could use this class to create a dictionary form an object and an object from a dictionary.
https://github.com/evermeer/EVReflection

HierarchicalCollectionView: One time sort?

I have an AdvancedDataGrid that relies on a HierarchicalCollectionView as it's dataProvider. What I'd like to do is sort the data when it is first loaded, but then disable the sort so that anything that is added after the initial load doesn't cause the grid to auto-sort again. I tried something like this:
this._myDataProvider = new HierarchicalCollectionView(
new HierarchicalData(this._model.rootTasks));
var mySort:Sort = new Sort();
mySort.fields = [new SortField("startDate")];
this._tasksDataProvider.sort = taskSorting;
this._tasksDataProvider.refresh();
this._tasksDataProvider.sort = null;
But setting the sort to null just leaves the data unsorted. I guess what I'm asking is: how can I sort the underlying hierarchical data since it seems setting the sort property will keep it dynamically sorting. Thanks for any help you can provide.
Personally, I would change the sort order when you're getting the data. Either it's done on the server side or when you parse the data (ie. in your model). You can do a one time sort using Array with sortOn.
you can
1. sort the original data with sort function,
2. clone content and put it to a new collection with no sort (be careful do and make a manual clone),
3. just use the new data collection.
I had the same kind of problem until I realized that the sorting with Sort object does not change the "physical" ordering of the items within the Collection, so when you remove the Sort, the next refresh reverts the view to the actual "physical" ordering.
Similarily as stated above, I solved by it by cloning the sub-collections into sorted order this way:
public static function buildPositionSort():Sort
{
var dataSortField:SortField = new SortField();
dataSortField.name = "position";
dataSortField.numeric = true;
dataSortField.descending = false;
var sort:Sort = new Sort();
sort.fields = [ dataSortField ];
return sort;
}
/**
* This method is used to create a clone of ArrayCollection, because sorting does not
* actually change the physical ordering of the items.
*/
public static function createSortedArrayCollectionCopy(source:ArrayCollection):ArrayCollection
{
var target:ArrayCollection = new ArrayCollection();
source.sort = buildPositionSort();
source.refresh();
for each (var item:Object in source)
{
if (item.children != null) item.children = createSortedArrayCollectionCopy(item.children);
target.addItem(item);
}
return target;
}

Assign Xml Values to dynamically created components

xml values are stored in 'arr' array collection. depending on the length of the array stored, the below described components are created and assign those values to relevant components dynamically.
For Example:
AdvanceSearch.mxml is one component, and another components as advanceSearchAtom.mxml. within 'advanceSearchAtom' has some sub components as textbox, combo box etc,. we can add many advanceSearchAtom.mxml inside 'AdvanceSearch.mxml'.
var container : AdvanceSearch;
var adv : advanceSearchAtom;
for(var i:int=0;i<arr.length;i++) {
adv = new advanceSearchAtom();
adv.field.text = arr[i].field;
adv.value.selectedIndex = arr[i].value;
container.addChild(adv);
}
Please let me know if anybody come across this type of problem. if any relevant link is appreciable. Thanks in advance
You didn't mention it, but I guess the problem is that you're getting null reference error (1009) on the following lines:
adv.field.text = arr[i].field;
adv.value.selectedIndex = arr[i].value;
Am I right?
This is because field and value are not yet created. As per the default component instantiation, Flex creates the children only when it is required - i.e., when it is to be displayed.
You can either listen to the creationComplete event of the AdvanceSearchAtom component and update the values from there; or have Binadble public variables in the AdvanceSearchAtom class, bind them to field.text and value.selectedIndex and assign xml values to those variables in the loop.
Using creation complete:
public var container:AdvanceSearch;
public var searchItems:Array = [];
public var arr:Array;
//assuming that arr has been initialized with xml values.
var adv:AdvanceSearchAtom;
for(var i:int=0;i<arr.length;i++) {
adv = new AdvanceSearchAtom();
adv.addEventListener(FlexEvent.CREATION_COMPLETE, onAtomCreated);
container.addChild(adv);
searchItems.push(adv);
}
public function onAtomCreated(e:Event):void
{
var adv:AdvanceSearchAtom = e.currentTarget as AdvanceSearchAtom;
if(!adv)
return;
var index:Number = searchItems.indexOf(adv);
adv.field.text = arr[index].field;
adv.value.selectedIndex = arr[index].value;
}
Using data binding:
Inside AdvanceSearchAtom.mxml
<mx:TextInput id="field" text="{textValue}"/>
<mx:ComboBox id="value" selectedIndex="{comboIndex}"/>
In the script block of AdvanceSearchAtom.mxml
[Bindable]
public var textValue:String;
[Bindable]
public var comboIndex:Number;
var adv:AdvanceSearchAtom;
In the AdvanceSearch class:
for(var i:int=0;i<arr.length;i++) {
adv = new AdvanceSearchAtom();
container.addChild(adv);
adv.field.text = arr[i].field;
adv.value.selectedIndex = arr[i].value;
}

AS3: Whats determines the order of: for..in

OK I am looping through the properties in an object like so:
private var _propsList:Object = {'Type':'product_type'
,'Kind':'product_type_sub'
,'Stone':'primary_stone'
,'Stone Color':'primary_stone_sub'
,'Metal':'metal_type'
,'Brand':'product_brand'};
for(key in _propsList)
{
val = _propsList[key];
trace(key +" = "+ val);
}
I am expecting the first trace to be Type = property_type since that is the first one defined in the array, however it is coming up random everytime. I guess this is because my keys are strings and not integers, however is there a way to specify the order it loops through them?
Thanks!!
You can't rely on for (v in someObject) ... to return things in a predictable order, no.
Depending on your specific situation, you could just use an array to hold the keys, and just iterate through that:
private var keys:Array = ["Type", "Kind", "Stone", "Stone Color", "Metal", "Brand"];
private function iterate():void
{
for each (var k:String in keys)
{
trace(_propsList[k]);
}
}
Maybe a bit obvious or non-elegant, but it'd get the job done. :)
you could hack it by classing-out your "_propsList" object creating an array inside of the newly created PropsList class that references the properties in order. At that point, you could run a FOR loop on the array and get your properties in order.
OR, you could have a function inside that new class that would return an Array of those properties. like this:
public function getProps():Array {
return [myPropertyOne, myPropertyTwo, myPropertyThree];
}
In general, I think this is a case where you shouldn't depend on a particular behavior from the framework/language you are using. This type of behavior is generally poorly documented and can change from version to version.
If you really need a specific retrieval order, I would create a wrapper class as jevinkones suggested above. Maybe there's even a utility class in the framework somewhere to accomplish this (Dictionary, etc.?)
HTH,
Karthik
I found this link that gives some background:
Subtle Change in for..in Loops for ActionScript 3
This question is actually a dup of this one.
How about using an array representation like this:
var _propsList:Array = [
['Type', 'product_type'],
['Kind', 'product_type_sub'],
['Stone', 'primary_stone'],
['Stone Color', 'primary_stone_sub'],
['Metal', 'metal_type'],
['Brand', 'product_brand']
];
for(var i in _propsList) {
var elem = _propsList[i];
var key = elem[0];
var val = elem[1]
}

Get item by index in a tree control

I'm doing a drag and drop operation on a tree using some help from Adobe's quick Starts:
http://www.adobe.com/devnet/flex/quickstart/working_with_tree/
The code suggested is roughly this:
var dropTarget:Tree = Tree(evt.currentTarget);
var i:int = dropTarget.calculateDropIndex(evt);
myTree.selectedIndex = i;
var node:XML = myTree.selectedItem as XML;
var drugXML:XML = XML(Tree(evt.dragInitiator).selectedItem);
if(node.localName() != drugXML.localName()){
DragManager.showFeedback(DragManager.NONE);
return;
}else{
DragManager.showFeedback(DragManager.COPY);
}
This is all well and good, but I don't like the way it is selecting(highlighting) each item in the tree I'm dropping on, it's less clear where the item is going to drop. I want to implement this without the selection but there doesn't seem to be a way to get the XML of the node using just the index. I would like something like this:
var dropTarget:Tree = Tree(evt.currentTarget);
var i:int = dropTarget.calculateDropIndex(evt);
var node:XML = myTree.itemAt(i) as XML;
//AFAIK itemAt(i) or anything like it does not exist
var drugXML:XML = XML(Tree(evt.dragInitiator).selectedItem);
if(node.localName() != drugXML.localName()){
DragManager.showFeedback(DragManager.NONE);
return;
}else{
DragManager.showFeedback(DragManager.COPY);
}
So does anyone know what function is, or what I can do to extend the tree to have a function, like "itemAt(i)"
Thanks
~Mike
EDIT: I forgot to post that my current workaround is setting the selectedIndex = -1 after I get my node:XML. I'm afraid that if something bogs on the processor the user may see it select then deselect.
Much simpler, though there may be gotchas:
var index:int = ...
var renderer:IListItemRenderer = tree.indexToItemRenderer(index);
var item:Object = renderer.data;
This won't work if the index is offscreen (since there might not be an active itemRenderer); shouldn't be an issue for drag and drop.
If your dataProvider is explicitly or implicitly a collection (see the linked docs for conversion rules), you should be able to use getItemAt to resolve the index.
It appears that an XML provider will be converted implicitly:
var tree:Tree = ...;
var data:XML = ...;
tree.dataProvider = data; // could just as well be from MXML
var provider:ICollectionView = tree.collection; // tree converted it for us
trace(provider.getItemAt(index));
If you have something other than the convertible types (XML, Array, etc.), you might consider wrapping your dataProvider in an XMLListCollection or what have you to gain access to that method.
The above is aiming in the right direction but missing.
Turns out you don't want the dataProvider since Tree overrides that; you want the collection property (protected). So you could override Tree and provide an indexToItem method to go with the thisToThat methods already present in Tree.

Resources