Implementing Comparator Sorting with QuickSort Algorithm

This shows how to implement a block based sorting with the quicksort algorithm.

in response to this Stackoverflow question

Example Usage

#import <Foundation/Foundation.h>
#import "NSArray+Comparator"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSArray *array = @[@4, @9, @2, @1, @7];
        array = [array vs_sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2)
        {
            return  [obj1 compare:obj2];
        }];
    }
    return 0;
}

Results in

( 1, 2, 4, 7, 9 )

This implements a quicksort algorithm that uses a comparator block to determin the ordering.

NSArray+Comparator

#import "NSArray+FunctionalTools.h"
#import "NSArray+RandomUtils.h"

@interface NSArray (Comparator)
-(NSArray *)vs_sortedArrayUsingComparator:(NSComparisonResult(^)(id obj1, id obj2))comparator;
@end
@implementation NSArray (Comparator)
-(NSArray *) quicksortUsingComparator:(NSComparisonResult(^)(id obj1, id obj2))comparator
{
    NSArray *array = [self copy];
    if ([array count]<2) {
      return [array copy];
    }
    id pivot = [array randomElement];
    NSMutableArray *array2= [NSMutableArray array];

    array = [array arrayByPerformingBlock:^id(id element) { return element;}
                      ifElementPassesTest:^BOOL(id element) { return comparator(element, pivot) == NSOrderedAscending;}
                         elsePerformBlock:^    (id element) { if (element!=pivot) [array2 addObject:element];}
            ];
    return [[[array quicksortUsingComparator:comparator]
                                    arrayByAddingObject:pivot]
                                        arrayByAddingObjectsFromArray:[array2 quicksortUsingComparator:comparator]];
}

-(NSArray *)vs_sortedArrayUsingComparator:(NSComparisonResult(^)(id obj1, id obj2))comparator
{
    return [self quicksortUsingComparator:comparator];
}
@end

NSArray+FunctionalTool

#import <Foundation/Foundation.h>

typedef id (^VSTestBlock)(id element);
@interface NSArray (FunctionalTools)
- (NSArray*)filter:(BOOL(^)(id element))filterBlock;

+(NSArray *)arrayByPerformingBlock:(id(^)(NSInteger index))performBlock
                withIndexFromRange:(NSRange)range;

- (NSArray *)arrayByPerformingBlock:(id  (^)(id element))performBlock;

- (NSArray *)arrayByPerformingBlock:(id  (^)(id element))performBlock
                ifElementPassesTest:(BOOL(^)(id element))testBlock;

- (NSArray *)arrayByPerformingBlock:(id   (^)(id element))performBlock
                ifElementPassesTest:(BOOL (^)(id element))testBlock
                   elsePerformBlock:(void (^)(id element))elseBlock;

- (NSArray *)arrayByPerformingBlock:(id   (^)(id element))performBlock
                ifElementPassesTest:(BOOL (^)(id element))testBlock
                   elsePerformBlock:(void (^)(id element))elseBlock
                           succeded:(void (^) ())succesBlock
                             failed:(void (^)(id element))failedBlock
                   stopOnFailedTest:(BOOL)stop;


-(NSSet *)setByPerformingBlock:(id  (^)(id element))performBlock
           ifElementPassesTest:(BOOL (^)(id element))testBlock;

- (NSDictionary *)dictionaryByFilteringWithBlocks:(NSArray *)filterBlocks forKeys:(NSArray *)keys;

-(NSArray *)arrayOfArraysWithColumnWidth:(NSUInteger)width;


-(void)performBlock:(void (^) (id element))block;
@end
//
//  NSArray+FuntionalTools.m
//  arraytools
//
//  Created by vikingosegundo on 28.08.11.
//  Copyright (c) 2011 vikingosegundo. All rights reserved.
//

#import "NSArray+FunctionalTools.h"

@implementation NSArray (FunctionalTools)
-(NSArray *)filter:(BOOL (^)(id))filterBlock
{
  NSMutableArray *filteredArray = [NSMutableArray array];
  for (id element in self)
    if (filterBlock(element))
      [filteredArray addObject:element];
  return [NSArray arrayWithArray:filteredArray];
}

+(NSArray *)arrayByPerformingBlock:(id (^)(NSInteger))performBlock
                withIndexFromRange:(NSRange)range
{
  NSMutableArray *array = [NSMutableArray array];
  for (NSUInteger i = range.location; i< range.location+range.length; ++i) {
    [array addObject:performBlock(i)];
  }
  return array;
}


- (NSArray *)arrayByPerformingBlock:(id (^)(id element))performBlock
{
  return [self arrayByPerformingBlock:performBlock
          ifElementPassesTest:^BOOL(id element) { return  YES;}];
}


- (NSArray *)arrayByPerformingBlock:(id(^)(id element))performBlock
                ifElementPassesTest:(BOOL(^)(id element))testBlock
{

  return [self arrayByPerformingBlock:performBlock
                  ifElementPassesTest:testBlock
                     elsePerformBlock:^(id element){;}];
}


-(NSArray *)arrayByPerformingBlock:(id (^)(id))performBlock
               ifElementPassesTest:(BOOL (^)(id))testBlock
                  elsePerformBlock:(void (^)(id))elseBlock
{
  NSMutableArray *array = [NSMutableArray array];
  for (id element in self)
    if(testBlock(element))
      [array addObject:performBlock(element)];
    else
      elseBlock(element);
  return array;
}


- (NSArray *)arrayByPerformingBlock:(id   (^)(id element))performBlock
                ifElementPassesTest:(BOOL (^)(id element))testBlock
                   elsePerformBlock:(void (^)(id element))elseBlock
                           succeded:(void (^)())succesBlock
                             failed:(void (^)(id element))failedBlock
                   stopOnFailedTest:(BOOL)stop

{
  NSMutableArray *array = [NSMutableArray array];
  for (id element in self){
    if(testBlock(element))
      [array addObject:performBlock(element)];
    else{
      elseBlock(element);
      if (stop) {
        failedBlock(element);
        break;
      }
    }
  }

  if (!stop) {
    succesBlock();
  }
    return array;
}

-(NSSet *)setByPerformingBlock:(id   (^)(id))performBlock
           ifElementPassesTest:(BOOL (^)(id))testBlock
{
    NSMutableSet *set = [NSMutableSet setWithArray:self];
    NSMutableSet *newSet = [NSMutableSet set];
    for (id element in set){
        if (testBlock(element)) {
            [newSet addObject:performBlock(element)];
        }
    }
    if ([newSet count]<1)
        return nil;
    return [NSSet setWithSet:newSet];
}

-(void)performBlock:(void (^)(id))block
{
  for(id element in self) {
    block(element);
  }
}


- (NSDictionary *)dictionaryByFilteringWithBlocks:(NSArray *)filterBlocks forKeys:(NSArray *)keys
{
  NSMutableDictionary *result = [NSMutableDictionary dictionaryWithCapacity:[keys count]];
  for (id key  in keys) {
    [result setObject:[NSMutableArray array] forKey:key];
  }
  for (id element in self) {
    for (NSUInteger i=0; i < [filterBlocks count]; i++) {
      BOOL (^block)(id element)  = [filterBlocks objectAtIndex:i];
      if (block(element))
        [(NSMutableArray *)[result objectForKey:[keys objectAtIndex:i]] addObject:element];
    }
  }
  return result;
}


-(NSArray *)arrayOfArraysWithColumnWidth:(NSUInteger)width
{
    NSAssert(width > 0, @"width need to be 1 or greater");

    NSMutableArray *mArray =[NSMutableArray array];
    [self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        if (idx % width == 0) {
            [mArray addObject:[NSMutableArray array]];
        }
        [[mArray objectAtIndex:[mArray count]-1] addObject:obj];
    }];
    return mArray;
}


@end

NSArray+RandomUtils

@interface NSArray (RandomUtils)
-(id)randomElement;

@end

@interface NSMutableArray (RandomUtils)
-(void)shuffle;
@end
#import "NSArray+RandomUtils.h"

@implementation NSArray (RandomUtils)

-(id)randomElement
{
  if ([self count] < 1) return nil;
  int randomIndex = arc4random_uniform((int)[self count]);
  return [self objectAtIndex:randomIndex];
}

@end

Download gist