Is there in Flex any utility to get object nested property using dot separated string like this:
SomeUtil.getObjectProperty(object, "child.property");
You can roll your own, assuming object is a dynamic object :
public function getObjectProperty(object:Object, property:String):Object
{
var parts:Array = property.split(".");
if(parts && parts.length == 2 && object && object[parts[0]] && object[parts[0]][parts[1]])
{
return object[parts[0]][parts[1]];
}
return null;
}
Here is another one that will work with different strings :
private function test(e:Event = null):void
{
var obj:Object = {
"child": {
"property":1
},
"anotherproperty": 2
};
var test1:Object = getObjectProperty(obj, "anotherproperty");
var test2:Object = getObjectProperty(obj, "child.property");
}
public function getObjectProperty(object:Object, property:String):Object
{
var parts:Array = property.split(".");
var returnProp:Object = null;
for (var i:int = 0; i < parts.length; i++)
{
if(object[parts[i]])
{
returnProp = object[parts[i]];
object = returnProp;
}
else
return null;
}
return returnProp;
}
Related
From what I understand:
If I want to hittest two non-array objects, the code would be:
if(player.hitTestObject(enemy))
{
}
The same code would also work if I was testing a non-array object with every object in an array.
But what do I do if I want to hittest every object in one array with every object in a second array?
Here's a resource-friendly one, if all you want to know is whether ANY of the items hit ANY other item:
public function anyItemWasHit( arr1:Array, arr2:Array ) : Boolean {
for each( var item:DisplayObject in arr1)
if( itemHitArrItem( item, arr2 ) )
return true;
return false;
}
private function itemHitArrItem( item:DisplayObject, arr:Array ) : Boolean {
for each( var arrItem:DisplayObject in arr )
if( item.hitTestObject( arrItem ) )
return true;
return false;
}
Based on the previous example, here's one that returns an array of ALL items that hit ANY other item:
public function itemsThatHitOtherItems( arr1:Array, arr2:Array ) : Array {
var items:Array = [];
for each( var item:DisplayObject in arr1 )
if( itemHitArrItem( item, arr2) )
items[items.length] = item;
return items;
}
And, finally, one that returns an array of all pairs (the item that hit another item, AND the item that was hit):
public function allPairsThatHitEachOther( arr1:Array, arr2:Array ) : Array {
var pairs:Array = [];
for each( var item:DisplayObject in arr1 ) {
var itemPairs:Array = allPairsForItem( item, arr2 );
if ( itemPairs.length > 0 )
pairs = pairs.concat( itemPairs );
}
return pairs;
}
private function allPairsForItem( item:DisplayObject, array:Array ) : Array {
var pairs:Array = [];
for each( var otherItem:DisplayObject in array )
if( item.hitTestObject( otherItem ) )
pairs[pairs.length] = [item, otherItem];
return pairs;
}
I'm guessing you're really just asking about the syntax. In this case it would be:
where:
indexOne goes from 0 to arrayOne.length - 1 (can be in a for loop)
indexTwo goes from 0 to arrayTwo.length - 1 (can also be in a nested for loop)
arrayOne[indexOne].hitTestObject(arrayTwo[indexTwo])
outerLoop: for (var i:int = 0; i < arr1.length; i++) {
var obj1:DisplayObject = arr1[i] as DisplayObject;
for (var j:int = 0; j < arr2.length; j++) {
var obj2:DisplayObject = arr2[j] as DisplayObject;
if (obj1.hitTestObject(obj2)) {
// these 2 hit each other
break outerLoop;
}
}
}
I am writing a match three engine and I succeed in creating the matching with using huge loops to find the matching items. Any ideas on how to fill the empty spaces with the items ( dropping down into the empty spaces ) and creating new items without excessive looping and if statements?
Here is my relavant code so far.
public var rows:uint = 8;
public var cols:uint = 7;
public var cell:Array = new Array();
public var plot:Array = new Array();
public var height:int;
public var width:int;
public var relativePositions:Array = [{name:'top', position:-1}, {name:'bottom', position:1}, {name:'left', position:rows*-1}, {name:'right', position:rows*1}];
public var dictionary:Dictionary = new Dictionary();
public var matches:Array = new Array();
public function createGrid(target:*, displayObject:*, spacer:int) : void {
var iterator:uint = 0;
for(var c:uint = 0;c<cols;c++){
for(var r:uint = 0;r<rows;r++){
cell[iterator] = createGamePiece();
Sprite(cell[iterator]).name = String(iterator);
Sprite(cell[iterator]).addEventListener(MouseEvent.CLICK, _handleGamePiece_CLICK);
Sprite(cell[iterator]).addEventListener(MouseEvent.MOUSE_OVER, _handleGamePiece_MOUSE_OVER);
Sprite(cell[iterator]).addEventListener(MouseEvent.MOUSE_OUT, _handleGamePiece_MOUSE_OUT);
cell[iterator].y = cell[iterator].height * r + (spacer*r);
cell[iterator].x = cell[iterator].width * c + (spacer*c);
GamePiece(cell[iterator]).positionX = cell[iterator].x;
GamePiece(cell[iterator]).positionY = cell[iterator].y;
GamePiece(cell[iterator]).positionRow = r;
GamePiece(cell[iterator]).positionCol = c;
target.addChild(cell[iterator]);
dictionary[String(iterator)] = cell[iterator]
iterator++
}
}
}
public function findRelativeMatches(targetSprite:Sprite) : void {
targetSprite.alpha = .5;
var rootPosition:Number = Number(targetSprite.name);
for ( var i:int = 0; i < relativePositions.length; i ++ ) {
var key:String = String(rootPosition + relativePositions[i].position);
// to do >> Not hardcoded to 'Pig'
if (findSprite(key) != null && GamePiece(targetSprite).color == GamePiece(findSprite(key)).color && GamePiece(findSprite(key)).found == false) {
var sprite:Sprite = findSprite(key);
sprite.alpha = .5;
GamePiece(sprite).found = true;
matches.push(sprite);
findRelativeMatches(sprite);
};
};
targetSprite.addEventListener(MouseEvent.MOUSE_OUT, function() : void {
if ( matches.length != 0 ) {
for ( var j:int = 0 ; j < matches.length ; j++ ) {
Sprite(matches[j]).alpha = 1;
GamePiece(matches[j]).found = false;
}
matches.splice(0);
}
});
}
public function findSprite(key:String) : Sprite {
var sprite:Sprite;
dictionary[key] != undefined ? sprite = dictionary[key] : null;
return sprite;
}
protected function _handleGamePiece_CLICK(event:MouseEvent):void
{
for ( var j:int = 0 ; j < matches.length ; j++ ) {
var sprite:Sprite = matches[j];
view.removeChild(matches[j]);
}
matches.splice(0);
}
public function createGamePiece() : Sprite {
var gamePiece:GamePiece = new GamePiece();
return gamePiece;
}
You want to collapse your grid downwards, right? The common algorithm is going from the bottom of every row upwards, having one index of the first empty space found, and the other for the first occupied space above empty space, then exchange those values once found, iterating to the top. But you don't store the grid in any accessible form! You should create a grid object, say a vector of vectors of sprites, and assign values in it as you move pieces around. Like this:
var GRID:Vector.<Vector.<Sprite>>; // this should be allocated at createGrid
// populate GRID with your sprites once generated:
// put the following into your inner loop in CreateGrid:
GRID[r][c]=cell[iterator];
// and the following into your removal of matches[] sprites:
GRID[GamePiece(sprite).positionRow][GamePiece(sprite).positionCol]=null; // release link from GRID
// now to move grid objects:
function DropAll():void {
var i:int;
var j:int;
for (i=GRID.length-1;i>=0;i--) {
var lastEmpty:int=-1;
for (j=GRID[i].length-1;j>=0;j--) {
if (GRID[i][j]) {
if (lastEmpty>0) {
GRID[i][lastEmpty--]=GRID[i][j];
// relocate your sprite properly here
GRID[i][j]=null;
} // else we're still at full part of grid, continue
} else if (lastEmpty<0) lastEmpty=j;
}
}
}
To properly instantiate GRID you need to allocate vectors of desired length that are filled with "null" values. Also, "GRID" itself is a Vector, and needs to be instantiated too.
GRID=new Vector.<Vector.<Sprite>>();
for (i=0;i<rows;i++) {
var a:Vector.<Sprite>=new Vector.<Sprite>(cols);
GRID.push(a);
}
After you do this, you fill the GRID by directly assigning links in it, like GRID[r][c]=gameObject;
This is actually what I wanted. A way to collapse WITHOUT iterating over the entire board. This way I JUST loop through the items that have been removed.
protected function _handleGamePiece_CLICK(event:MouseEvent):void
{
for ( var j:int = 0 ; j < matches.length ; j++ ) {
var oldSprite:Sprite = matches[j];
moveAllPiecesDown(oldSprite);
view.removeChild(oldSprite);
oldSprite = null;
}
matches.splice(0);
}
private function moveAllPiecesDown(oldSprite:Sprite):void
{
var piecesAbove:int = GamePiece(oldSprite).positionRow;
var index:int = int(oldSprite.name);
for( var i:int = 0; i < piecesAbove; i ++ ) {
var spriteAbove:Sprite = Sprite(view.getChildByName(String(index-(1+i))));
if(spriteAbove) {
spriteAbove.y = spriteAbove.y + spriteAbove.height + 1;
spriteAbove.name = String(Number(spriteAbove.name)+1);
GamePiece(spriteAbove).textField.text = spriteAbove.name;
delete dictionary[spriteAbove.name];
dictionary[spriteAbove.name] = spriteAbove;
}
}
}
I have the following object:
var users:Object= new Object();
users[0]["user_id"] = "1124";
users[0]["name"] = "ikke";
users[0]["age"] = "24";
users[0]["gender"] = "male";
users[1]["user_id"] = "1318";
users[1]["name"] = "test";
users[1]["age"] = "20";
users[1]["gender"] = "male";
var selectors:Object = new Object();
selectors["user_id"] = 1318;
selectors["gender"] = "male";
what i want is to use the selectors object in an if statement. In humans lanuguage it should be something like:
for (var index:String in users) {
If users[index]["gender"] == selectors[gender] && users[index]["user_id"] == "male" -> then trace "success".
}
The tricky part is that the selectors object is dynamic. Sometimes it can contain only 1 item , sometimes 3 items. Or it can also be null. In that case it should allways trace success. Anyone that can help me?
for(var i:int = 0; i < users.length; i++) {
var success:Boolean = true;
for(var key:String in selectors) {
if(users[i][key] != selectors[key]) {
success = false;
break;
}
}
if(success) {
trace('success for user ' + i);
}
}
when comparing simple arrays, i use something like the following function to concatenate and remove duplicates:
//Merge
public function merge(a1:Array, a2:Array):Array
{
var result:Array = a1.concat(a2);
var dictionary:Dictionary = new Dictionary();
for each (var item:Object in result)
dictionary[item] = true;
result = new Array();
for (var key:Object in dictionary)
result.push(key);
dictionary = null;
return result;
}
however, this approach doesn't work on complex arrays.
is there a well known algorithm, or is it even possible to write a function of recursion that can compare a Vector.<Object> with another? one that will always work even if the some objects being compared have additional key/value pairs?
[EDIT]
to be more clear, using a dictionary to determine if items in a simple array only works on primitive data types (int, number, string, etc.) or object references, so the above example works if it's passed 2 arrays resembling something like this:
var arr1:Array = new Array(1, 2, 3, 4, 5);
var arr2:Array = new Array(8, 7, 6, 5, 4);
resulting in a merged array with the following values:
1, 2, 3, 8, 7, 6, 5, 4
in contrast, i'm asking if it's possible to pass a function 2 complex arrays or Vector.<Object> all containing unique objects that may have identical key/value pairs and remove redundencies in the resulting Vector.<Object>. for example:
var vec1:Vector.<Object> = new Vector.<Object>();
vec1.push({city:"Montreal", country:"Canada"});
vec1.push({city:"Halifax", country:"Canada"});
var vec2:Vector.<Object> = new Vector.<Object>();
vec2.push({city:"Halifax", country:"Canada"});
vec2.push({city:"Toronto", country:"Canada"});
merging the above 2 vector objects would result in the following vector by determining and removing objects with identical key/value pairs:
{city:"Montreal", country:"Canada"}
{city:"Halifax", country:"Canada"}
{city:"Toronto", country:"Canada"}
i'm searching for an algorithm which could handle the removal of these similar objects without having to know about their specific key/value names or how many key/value pairs there are within the object.
Sure you can, you can build a similar example with any type of Vector:
public function mergeObjectVectors(v1:Vector.<Object>,
v2:Vector.<Object>):Vector.<Object>
{
var dictionary:Dictionary = new Dictionary();
var concat:Vector.<Object> = v1.concat(v2);
var result:Vector.<Object> = new Vector.<Object>();
for each(var i:Object in concat)
{
if (!dictionary[i])
{
dictionary[i] = true;
result.push(i);
}
}
return result;
}
However if you plan on accepting vectors of any type, it's different:
public function testing():void
{
var v1:Vector.<Object> = new Vector.<Object>();
v1.push({name:"Object 1"});
v1.push({name:"Object 2"});
// Vector w duplicates
var v2:Vector.<Object> = new Vector.<Object>();
var o:Object = {name:"Object 3"};
v2.push(o);
v2.push(o);
v2.push(o);
var resultVector:Vector.<Object> = mergeAnything(v1, v2, Class(Vector.<Object>));
var resultArray:Array = mergeAnything(v1, v2, Array);
var resultObject:Object = mergeAnything(v1, v2, Object);
}
public function mergeAnything(o1:Object, o2:Object, resultClass:Class):*
{
var dictionary:Dictionary = new Dictionary();
var result:Object = new resultClass();
var i:int;
for each(var o:Object in o1)
{
if (!dictionary[o])
{
dictionary[o] = true;
result[i++] = o;
}
}
for each(o in o2)
{
if (!dictionary[o])
{
dictionary[o] = true;
result[i++] = o;
}
}
return result;
}
The first example will be more resource-efficient.
EDIT:
This should do it, try it with your example:
public function mergeObjectVectors(v1:Vector.<Object>, v2:Vector.<Object>):Vector.<Object>
{
var concat:Vector.<Object> = v1.concat(v2);
var result:Vector.<Object> = new Vector.<Object>();
var n:int = concat.length;
loop:for (var i:int = 0; i < n; i++)
{
var objectToAdd:Object = concat[i];
var m:int = result.length;
for (var j:int = 0; j < m; j++)
{
var addedObject:Object = result[j];
if (this.areObjectsIdentical(objectToAdd, addedObject))
{
continue loop;
}
}
result.push(objectToAdd);
}
return result;
}
private function areObjectsIdentical(o1:Object, o2:Object):Boolean
{
var numComparisons:int = 0;
for (var s:String in o1)
{
numComparisons++;
if (o1[s] != o2[s])
{
return false;
}
}
for (s in o2)
{
numComparisons--;
}
return !numComparisons;
}
How can you trace the "depth" or stacking order of a display object with AS3?
I'm trying to figure out if my sprite is behind another sprite...
container.getChildIndex(displayObject);
but that will only tell you how deep it is, not necessarily if anything is in front of it.
Function comparing two DisplayObject instances to determine which one is at a higher "depth" on the display list:
private function higher(a:DisplayObject, b:DisplayObject):DisplayObject
{
// Parent chains
var ac:Array = [a];
var bc:Array = [b];
// Pointers to individual nodes
var an:DisplayObject = a.parent;
var bn:DisplayObject = b.parent;
while (an != null) {
ac.push(an);
an = an.parent;
}
while (bn != null) {
bc.push(bn);
bn = bn.parent;
}
var acl:int = ac.length;
var bcl:int = bc.length;
var n:int = Math.min(acl, bcl);
var i:int = 0;
for (; i < n; i++) {
an = ac[acl - i - 1];
bn = bc[bcl - i - 1];
// First uncommon ancestor
if (an != bn)
break;
}
var ca:DisplayObjectContainer = an.parent;
if (!ca)
return null;
if (ca.getChildIndex(an) > ca.getChildIndex(bn))
return a;
else
return b;
}
Note: If one of the objects is not on the display list, the function returns null. You can change it to return the other object instead.
You can almost certainly optimize this, but this is a first cut.
Just a refactored version of Manish answer using vectors and which won't return weird result if you ever call higher(a,a.parent).
parents() may be used for other purpose too :
public function higher(a:DisplayObject, b:DisplayObject):DisplayObject
{
var aParents:Vector.<DisplayObject> = parents(a);
var bParents:Vector.<DisplayObject> = parents(b);
var commonDepth:int = Math.min(aParents.length, bParents.length);
for (var depth:int = 0; depth < commonDepth; depth++)
if (aParents[depth] != bParents[depth])
break;
if (depth == 0 || depth == commonDepth)
return null;
var commonAncestor:DisplayObjectContainer = aParents[depth].parent;
if (commonAncestor.getChildIndex(aParents[depth]) > commonAncestor.getChildIndex(bParents[depth]))
return a;
else
return b;
}
private function parents(d:DisplayObject):Vector.<DisplayObject>
{
var result:Vector.<DisplayObject> = new Vector.<DisplayObject>;
while (d != null)
{
result.unshift(d);
d = d.parent;
}
return result;
}
private function getDepth(clip:DisplayObject):uint
{
var depth:uint = 0;
var currentClip:DisplayObject = clip;
while (currentClip.parent && currentClip.parent != this)
{
depth++;
currentClip = currentClip.parent;
}
return depth;
}
container.getChildIndex(child) should do it, it returns the index of the child
This is a revised version of what jauboux did from a version Manish did.
Namely, adding a null return value from highestOf() when depths match.
/**
* #param ifDepthsMatchReturnObjectA
* #return Whichever DisplayObject is higher on the display list.
* Optionally returns `null` if they're at the same depth.
*/
public function highestOf(a:DisplayObject, b:DisplayObject, ifDepthsMatchReturnObjectA:Boolean = false):DisplayObject
{
var aParents:Vector.<DisplayObject> = ancestorsOf(a);
var bParents:Vector.<DisplayObject> = ancestorsOf(b);
var commonDepth:int = Math.min(aParents.length, bParents.length);
for (var depth:int = 0; depth < commonDepth; depth++)
if (aParents[depth] != bParents[depth])
break;
if (depth == 0 || depth == commonDepth)
return null;
var commonAncestor:DisplayObjectContainer = aParents[depth].parent;
var aDepthOnCommonAncestor:int = commonAncestor.getChildIndex(aParents[depth]);
var bDepthOnCommonAncestor:int = commonAncestor.getChildIndex(bParents[depth]);
if (aDepthOnCommonAncestor > bDepthOnCommonAncestor)
return a;
else if (aDepthOnCommonAncestor < bDepthOnCommonAncestor)
return b;
else
return ifDepthsMatchReturnObjectA ? a : null;
}
/**
* #return Whether a is higher than b.
*/
public function isHigher(a:DisplayObject, b:DisplayObject):Boolean
{
return highestOf(a, b) === a;
}
/**
* #return All ancestors of given display.
*/
private function ancestorsOf(display:DisplayObject):Vector.<DisplayObject>
{
var result:Vector.<DisplayObject> = new Vector.<DisplayObject>;
while (display != null)
{
result.unshift(display);
display = display.parent;
}
return result;
}