Resolved Elegant way to cycle through NSArray from middle index value to the one preceding it?

Discussion in 'Mac Programming' started by GorillaPaws, Aug 17, 2011.

  1. GorillaPaws, Aug 17, 2011
    Last edited: Aug 17, 2011

    GorillaPaws macrumors 6502a

    GorillaPaws

    Joined:
    Oct 26, 2003
    Location:
    Richmond, VA
    #1
    I have an NSArray with 6 values in it. I'm trying to create 2 additional NSArrays that cycle through the values starting at indexes 2 and 4 respectively.

    In other words:

    Original NSArray's Index/Values

    0 - A
    1 - B
    2 - C
    3 - D
    4 - E
    5 - F

    Desired results:

    newArray1

    0 - C
    1 - D
    2 - E
    3 - F
    4 - A
    5 - B

    newArray2

    0 - E
    1 - F
    2 - A
    3 - B
    4 - C
    5 - D

    Is there an elegant way to loop "back around" through the values after starting midway through the index? I could obviously create 2 loops for each array, but this seems clumsy, and I was hoping for a smarter solution.
     
  2. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #2
    Maybe just using % to generate new indexes, and starting a few different counters?

    -Lee
     
  3. loadedsith, Aug 17, 2011
    Last edited: Aug 17, 2011

    loadedsith macrumors newbie

    Joined:
    May 22, 2010
    #3
    I agree with modulus (%) as the operative solution. You might try something like:

    (Pseudo code, not intended to work as is)

    iMax=NSArray.length;
    iMid=round((iMax/2));
    iStart=iMid;

    for(i=0; i<iMax; i++; ){
    newArray = NSArray[(iStart+i)%iMax];
    }

    Hope that helps (and that i didn't make too many errors)
     
  4. wlh99 macrumors 6502

    Joined:
    Feb 7, 2008
    #4
    Not sure if this will work, as I can't test it right now. I'm curious though so I may try it when I get home:

    Code:
    
    for (int i=0,int j=2,int k=4, int c=[myArray count] ; i<c; i++) {
    	[newArray1 addObject:[myArray objectAtIndex:j];
    	[newArray2 addObject:[myArray objectAtIndex:k];
    	j=(j+1)%c;
    	k=(k+1)%c;
    }
    
     
  5. GorillaPaws, Aug 17, 2011
    Last edited: Aug 17, 2011

    GorillaPaws thread starter macrumors 6502a

    GorillaPaws

    Joined:
    Oct 26, 2003
    Location:
    Richmond, VA
    #5
    Thanks to all who took the time to respond. I was hoping for some clever built-in solution to this, but your answers were very helpful in designing my own solution. I ended up designing it with greater flexibility in mind in case I wanted 6 arrays each shifted by 1 place or 2 arrays shifted by 3 places etc.

    Here is the method I wrote that takes a mutable array and the number of desired shifted arrays and returns a mutable array containing each shifted array (including the original array that isn't shifted).

    Code:
    -(NSMutableArray *) cycleValuesForArray: (NSMutableArray *)theArrayToShift quantityOfArrays: (NSInteger)theNumberOfArrays
    {
    	NSMutableArray *rMArrayOfArrays = [NSMutableArray array];
    	
    	NSInteger tMaxIndex = [theArrayToShift count];
    	if( (tMaxIndex % theNumberOfArrays) != 0 )
    	{
    		//	if the size of the array isn't evenly divisible by theNumberOfArrays, return nil array
    		rMArrayOfArrays = nil;	
    		return rMArrayOfArrays;
    	}
    	NSInteger tIndexShift = tMaxIndex / theNumberOfArrays;
    	NSInteger tIndexIterator = 0;
    	NSInteger tMidIndex = 0;
    	
    	for( NSInteger i = 0; i < theNumberOfArrays; i++ )
    	{
    		NSMutableArray *tShiftedValuesMArray = [NSMutableArray array]; 
    		
    		for( NSInteger j = 0; j < tMaxIndex; j++ )
    		{
    			tIndexIterator = (tMidIndex + j) % tMaxIndex;
    			[tShiftedValuesMArray insertObject: [theArrayToShift objectAtIndex: tIndexIterator]
    						   atIndex: j];
    		}
    		tMidIndex = (i + 1) * tIndexShift;
    		
    		[rMArrayOfArrays insertObject: tShiftedValuesMArray 
    				      atIndex: i];
    	}
    	return rMArrayOfArrays;
    }
    
    Thanks again for your help. I'm working on shuffling around keyframe animations in case anyone was wondering what this could possibly be useful for;
     
  6. PatrickCocoa macrumors 6502a

    Joined:
    Dec 2, 2008
    #6
    Unroll your own

    If you really only have six elements in your array, and that won't change, then unroll your loops:

    Code:
    NSArray newArray1, newArray2;
    newArray1 = [[NSArray alloc] init];
    newArray2 = [[NSArray alloc] init];
    [newArray1 initWithObjects: originalArray[2],
                             originalArray[3],
                              originalArray[4],
                              originalArray[5],
                              originalArray[0],
                              originalArray[1],
                             nil];
    
    [newArray2 initWithObjects: originalArray[4],
                             originalArray[5],
                              originalArray[0],
                              originalArray[1],
                              originalArray[2],
                              originalArray[3],
                             nil];
    
     
  7. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #7
    I guess I feel that this is a bit wasteful. I might make a MyShiftableArray that has your "base" array as a member, and a method that can get you a shifted element. Do you NEED different arrays that are shifted?

    -Lee
     
  8. GorillaPaws, Aug 17, 2011
    Last edited: Aug 17, 2011

    GorillaPaws thread starter macrumors 6502a

    GorillaPaws

    Joined:
    Oct 26, 2003
    Location:
    Richmond, VA
    #8
    I'm drawing a hexagon in a CAShapeLayer. I have an array of it's 6 NSPoint vertices. I'm working on creating CAEmitterLayers that trace the outline of the hexagon, leaving a trail behind to indicate it's been selected. I haven't decided if I want 1, 2, 3, or 6 of these emitterLayers and I probably won't know until I play with it for a while to see what looks best. I'm creating the paths for each respective emitterLayer's position attribute to use for the animation.

    My intent is to call this method with the baseArray and iterate through the resulting rMArrayOfArrays using the array of points at each iteration to create a path for each emitterLayer. I guess I could push some of this logic up into the method calling this one, and mutate an array the appropriate number of times, passing in the results to the respective paths, but doesn't my approach encapsulate the logic better, making the calling method a bit simpler and easier to read? Is it really that excessive to generate several autoreleased arrays? I always appreciate advice from people who know what they're doing, being self-taught means it's easy to acquire bad habits without someone constantly correcting your mistakes. Obviously just because it compiles and does the job, doesn't make it right.

    Thanks for taking the time to help.
     
  9. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #9
    In this case you're burning a few bytes of memory. I assume things might become grander in scope down the line. Making 1000 copies of a 10000 element arrays would be much worse, which is where my brain goes. I'd try to come up with something that wraps the single backing array and emulates the shift behind the scenes. It may not be practical here, it's just the way my brain tries to address problems.

    -Lee
     

Share This Page