views:

219

answers:

4

I have a data structure as the image below illustrates. I need to quickly figure out the index of the cells to the right or left of the highlighted cell group.

some data

You can see in the code below I am naively looping through ALL cells at every index to determine if there is a cell at the requested index. This works great when I have a few (hundred) cells, but breaks down quickly when I have thousands of cells.

In this particular case the highlighted group is mobile, and can only move to the index before or after the previous/next occupied cell. So groupMinX/maxX is the minimum and maximum x value it can move based on the position of other cells in the row.

            private var movingGroup:CellGroup; //selected group

 public function getCellAtIndex(index:int):ICell
 {
  for each(var cell:ICell in cells)
  {
   if(cell.index==index)
    return cell;
  }

  return null;
 }

 public function groupMinX(xPos:Number):Number
 {
  var index:int = xPos/cellSize;
  var cellsOnLeft:Array = getAllCellsOnLeft(index-1);
  if(cellsOnLeft.length > 0)
   return cellsOnLeft[cellsOnLeft.length-1].x + cellSize;
  return 0;
 }

 public function groupMaxX(xPos:Number):Number
 {
  var index:int = xPos/cellSize;
  var cellsOnRight:Array = getAllCellsOnRight(index);
  if(cellsOnRight.length > 0)
   return cellsOnRight[0].x;
  return (maxIndex)*cellSize;
 }

 private function getAllCellsOnLeft(ofIndex:int):Array
 {
  var index:int = 1;
  var cells:Array = [];
  while( ofIndex >= 0 )
  {
   var cell:ICell = getCellAtIndex(ofIndex);
   if(cell && !movingGroup.containsCell(cell))
    cells.unshift( cell );
   ofIndex--;
  }
  return cells;  
 }

 private function getAllCellsOnRight(ofIndex:int):Array
 {
  var index:int = 1;
  var cells:Array = [];
  while( index <= maxIndex )
  {
   var cell:ICell = getCellAtIndex( ofIndex + index );
   if(cell && !movingGroup.containsCell(cell))
    cells.push( cell );
   index++;
  }
  return cells;  
 }

What I am looking for is an efficient method for scanning/tracking the cells. The array I am looping through doesn't actually contain the blank cells, but it has the cells with the index property.

+1  A: 

Whoops in my tweet I added the wrong link. Use a linked list. Have your 'Cell' class implement a Linked List Node interface. No need to loop or use conditionals.

http://en.wikipedia.org/wiki/Linked%5Flist

Alan
this is the direction I am leaning towards and thought about from the beginning but was being lazy. Now that lazy is biting me in the ass ;)
Joel Hooks
using the linked list is almost instantaneous. Combined with Banang's search approach, I have a loopless solution. Cheers!
Joel Hooks
A: 

You could just pre-cache the indices of the cell on the left and the cell on the right ... Your choice of a data structure is non-ideal. Please describe what you are trying to do at a higher level. Was this data structure given to you, or did you come up with it?

Hamish Grubijan
I know the data structure is non-ideal. hence the post ;). The indices are dynamic so caching them doesn't work.
Joel Hooks
A: 

I'm not sure if I am missing something here but if this is a numerically index array of objects (cells), I think you could do something like...

cellsArr[cellsArr.indexOf(cellObj1) - 1] // previous cell    
cellsArr[cellsArr.indexOf(cellObj2) + 1] // get the cell after a "highlighted" cell
Chris Gutierrez
the index of a given cell does not correspond to its position in the array.
Joel Hooks
+1  A: 

As the list is ordered, I would suggest you do a binary search to find your wanted cell. Then, rather than looping through the elements to the left and the right, simply slice the array to form two new arrays of the left and right side.

Something like so, parhaps? (please excuse any syntactical errors, I don't know actionscript...)

private function search(array:Array, index:int, low:int, high:int) :int 
{ 
  if (high < low)
    return -1 
  var middle:int = low + ((high - low) / 2) 
  if (array[middle].index > index)
    return search(array, index, low, middle - 1)
  else if (array[middle].index < index)
    return search(array, index, middle + 1, high)
  else
    return middle 
}  

private function sliceBitsOff(index:int)
{
   var index:int = search(yourArray, 7, 0, yourArray.length-1)
   var rightArray:Array = yourArray.slice(0, index - 1)
   var leftArray:Array = yourArray.slice(index + 1, yourArray.length)
}
Banang
This is extremely interesting, and an approach that I hadn't considered. By slicing the array I have instant access to the neighbors without going through the bother of creating the linked list solution.
Joel Hooks
or at the very least this saves me from any looping what-so-ever to get my initial target cell. Combined with the linked list I have a no-loop solution
Joel Hooks
I edited your solution to add -1 to the array.length.
Joel Hooks
Ah, thanks. I always stumble on indexing outside of arrays. ;) Glad you liked it, good luck!
Banang