0

Here is my requirement :

forSaleSingleProperties array should contain dictionaries with no same attribute PARCELID

forSaleMultipleProperties array should contain an array of those dictionaries with same attribute PARCELID

forSalePropertiesArray is the base array containing all dict.

Note: Dictionaries contain various other attributes. I want all those with same PARCELID attribute

I don't understand what is wrong with this logic...

if (_forSaleSinglePropertiesArray==nil) {
    _forSaleSinglePropertiesArray = [[NSMutableArray alloc]initWithObjects: nil];
}

if (_forSaleMultiplePropertiesArray==nil) {
    _forSaleMultiplePropertiesArray = [[NSMutableArray alloc]initWithObjects: nil];
}

if (_forSalePropertiesArray!=nil) {
    if (_forSalePropertiesArray.count>1) {
        BOOL propertyObject1IsMultiple = NO;
        NSDictionary *propertyObject1;
        NSMutableArray *multiplePinArray = [[NSMutableArray alloc]initWithObjects: nil];
        for (int i=0; i<_forSalePropertiesArray.count; i++) {
            propertyObject1 = [_forSalePropertiesArray objectAtIndex:i];
            multiplePinArray = nil;
            multiplePinArray = [[NSMutableArray alloc]initWithObjects: nil];
            for (int j=i+1; j<_forSalePropertiesArray.count; j++) {
                NSDictionary *propertyObject2 = [_forSalePropertiesArray objectAtIndex:j];
                if ([propertyObject1 valueForKey:PARCEL_ID]==[propertyObject2 valueForKey:PARCEL_ID]) {
                    if (_forSaleMultiplePropertiesArray.count==0) {
                        [multiplePinArray addObject:propertyObject2];
                        propertyObject1IsMultiple = YES;
                        [_forSaleMultiplePropertiesArray addObject:multiplePinArray];
                    }else{
                        BOOL propFound = NO;
                        NSMutableArray *propArr;
                        NSInteger index = -1;
                        for(NSMutableArray *arr in _forSaleMultiplePropertiesArray){
                            if (![arr containsObject:propertyObject2]&&!propFound) {
                                [arr addObject:propertyObject2];
                                propertyObject1IsMultiple = YES;
                                propFound = YES;
                                index = [_forSaleMultiplePropertiesArray indexOfObject:arr];
                                propArr = [[NSMutableArray alloc]initWithArray:arr];
                            }
                        }
                        if (propArr!=nil) {
                            [_forSaleMultiplePropertiesArray replaceObjectAtIndex:index withObject:propArr];
                        }
                    }
                }
            }
            if (!propertyObject1IsMultiple) {
                [_forSaleSinglePropertiesArray addObject:propertyObject1];
            }
        }
    }
}
Fogmeister
  • 76,236
  • 42
  • 207
  • 306
PrasadW
  • 397
  • 6
  • 19
  • 1
    That is some pretty ugly code. – Hot Licks Oct 22 '14 at 02:07
  • 2
    You should be able to do this using `NSSet` and/or `NSPredicate` to filter and group the data http://stackoverflow.com/questions/1439564/iphone-getting-unique-values-from-nsarray-object – Flexicoder Oct 22 '14 at 16:38
  • 1
    Dat code though... The complexity. The Ifs. The loops. Just wow. Lemme try get my head around this. I reckon (from the title) I can do it in around 10 lines... Hehe :D – Fogmeister Oct 22 '14 at 16:46
  • Agreed its an ugly code, thats why I came here, just needed a simpler mechanism – PrasadW Oct 23 '14 at 00:51

4 Answers4

1

OK so...

I'm leaving this as a placeholder.

  1. Sort the parent array by PARCELID.

  2. Iterate array.

  3. Sort into two piles.

... or something. Will write it later.

Fogmeister
  • 76,236
  • 42
  • 207
  • 306
0

I would have done it more like this:

- (void)solution {
    self.forSaleSinglePropertiesArray = [[NSMutableArray alloc] init];
    self.forSaleMultiplePropertiesArray = [[NSMutableArray alloc] init];

    NSDictionary *partitionedProperties = partitionPropertiesByParcelID(self.forSalePropertiesArray);
    [self dividePropertiesIntoSingleAndMultiple:partitionedProperties];
}

NSDictionary *partitionPropertiesByParcelID(NSArray *properties) {
    NSMutableDictionary *result = [[NSMutableDictionary alloc] init];
    for (NSDictionary *property in properties) {
        id parcelID = property[PARCEL_ID];
        NSMutableArray *parcels = result[parcelID];
        if (parcels == nil) {
            parcels = [[NSMutableArray alloc] init];
            [result setObject:parcels forKey:parcelID];
        }
        [parcels addObject:property];
    }
    return result;
}

- (void)dividePropertiesIntoSingleAndMultiple:(NSDictionary *)partitionedProperties {
    for (NSArray *properties in partitionedProperties.allValues) {
        if (properties.count == 1) {
            [self.forSaleSinglePropertiesArray addObject:properties[0]];
        }
        else {
            assert(properties.count > 1);
            [self.forSaleMultiplePropertiesArray addObject:properties];
        }
    }
}

First the code creates a Dictionary where the keys are parcel IDs and the values are arrays of properties with that parcle ID. Then it goes through that dictionary and puts a representative of each parcel ID into either the single or multiple array.

I feel that this code is easier to understand, and I strongly suspect that if you do performance metrics on the above code and your answer, that this code will have better performance over large data sets. I believe this is true because your answer seems to have O(n^2) performance while mine is O(n). (Read up on "Big-O notation" if you aren't sure what this means.)

Also, are you sure that your answer actually works for all data sets? Removing objects out of an array while iterating over it throws up a huge red flag in my book.

Daniel T.
  • 32,821
  • 6
  • 50
  • 72
0

I liked @Fogmeister's sort idea so much, I implemented it:

- (void)sortSolultion {
    self.forSaleSinglePropertiesArray = [[NSMutableArray alloc] init];
    self.forSaleMultiplePropertiesArray = [[NSMutableArray alloc] init];

    NSArray *forSaleArray = [self.forSalePropertiesArray sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
        return [obj1[PARCEL_ID] compare:obj2[PARCEL_ID]];
    }];
    id currentParcelID = nil;
    self.propertiesWithCurrentParcelID = [[NSMutableArray alloc] init];
    for (NSDictionary *property in forSaleArray) {
        if (self.propertiesWithCurrentParcelID.count == 0) {
            currentParcelID = property[PARCEL_ID];
        }
        else if (![property[PARCEL_ID] isEqual: currentParcelID]) {
            [self placeCurrentPropertiesInCorrectArray];
            currentParcelID = property[PARCEL_ID];
        }
        [self.propertiesWithCurrentParcelID addObject:property];
    }
    [self placeCurrentPropertiesInCorrectArray];
}

- (void)placeCurrentPropertiesInCorrectArray {
    if (self.propertiesWithCurrentParcelID.count > 1) {
        [self.forSaleMultiplePropertiesArray addObject:self.propertiesWithCurrentParcelID];
    }
    else if (self.propertiesWithCurrentParcelID.count == 1) {
        [self.forSaleSinglePropertiesArray addObject:self.propertiesWithCurrentParcelID[0]];
    }
    [self.propertiesWithCurrentParcelID removeAllObjects];
}

This solution has a slightly higher cyclomatic complexity than my previous solution, and indeed it was harder to get it error free than my first one. But this solution has a smaller allocation footprint. Both seem to have the same big-O complexity.

Daniel T.
  • 32,821
  • 6
  • 50
  • 72
-1

Alright I got it right myself.

Updated Code to a much simpler and fast mechanism below:

if (_forSaleSinglePropertiesArray==nil) {
    _forSaleSinglePropertiesArray = [[NSMutableArray alloc]initWithObjects: nil];
}
if (_forSaleMultiplePropertiesArray==nil) {
    _forSaleMultiplePropertiesArray = [[NSMutableArray alloc]initWithObjects: nil];
}
if (_forSalePropertiesArray!=nil) {
    NSMutableDictionary *samePropsDict = [[NSMutableDictionary alloc]init];
    for (NSDictionary* dict in _forSalePropertiesArray) {
        NSMutableArray *samePropsArray = [samePropsDict objectForKey:[dict valueForKey:PARCEL_ID]];
        if (samePropsArray==nil) {
            samePropsArray = [[NSMutableArray alloc]init];
        }
        [samePropsArray addObject:dict];
        [samePropsDict setObject:samePropsArray forKey:[dict valueForKey:PARCEL_ID]];
    }
    for (NSString *key in [samePropsDict allKeys]) {
        NSArray *arr = [samePropsDict objectForKey:key];
        if (arr.count>1) {
            [_forSaleMultiplePropertiesArray addObject:arr];
        }else{
            [_forSaleSinglePropertiesArray addObject:[arr firstObject]];
        }
    }
}
PrasadW
  • 397
  • 6
  • 19
  • FYI. The above will fail with a three property array defined as follows: `@[propertyWithParcelID1, propertyWithParcelID2, propertyWithParcelID1]`. It will never find the single property. – Daniel T. Oct 22 '14 at 16:38
  • I updated the code to find it, it works fine now even for single properties – PrasadW Oct 23 '14 at 11:41
  • Edit the above code too. I'm giving a talk on your problem today seeing how you implemented your algorithm would help. – Daniel T. Oct 23 '14 at 13:37
  • It still fails, and for the same reason. `@[propertyWithParcelID1, propertyWithParcelID2, propertyWithParcelID1]` should end up with one property in the single property array @[propertyWithParcelID2] and one array of two properties in the multiple properties array @[[propertyWIthParcelID1, propertyWithParcelID1]]. It successfully puts the two properties in the multiplePropertyArray, but it looses the property that was supposed to go in the single property array. – Daniel T. Oct 24 '14 at 02:04
  • If you insist on removing objects from an array that you are iterating over, you will have to be way more careful than you are being. – Daniel T. Oct 24 '14 at 02:06