Replace objects in NSMutableArray contains NSMutableDictionary with NSIndexSet - ios

I've a main array (arrayEvents) contains list of events with fields like, start_date, event_id, event_name, many events can be possible on same date but different times. So I'm taking only unique dates (arrayDates) to check and compare it with the current date (currentDate its range from 1st day of month to last day of month) to show event on particular dates. I'm done up to here. Now my problem is that, I need to update arrayEvents for later comparisons so I am applying the following logic ...but I am in the middle of the sea and don't know where to go!
for(int i=0;i<[arrayDates count];i++) {
NSDate *obj = [arrayDates objectAtIndex:i];
if([obj isEqualToDate:currentDate]) {
//This date has some events, we've done up to here.
//Now taking indexes of same objects (that's same dates objects) from the arrayEvents
NSIndexSet *indexesOfObjects = [arrayDates indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
return [arrayEvents containsObject:obj];
}];
if([indexesOfObjects count] > 1) {
//We found some objects into arrayEvents, now I have to update
//those objects into arrayEvents.
//How to achieve this?
//My plan was to iterate each indexes and make update on arrayEvents
//but I can't do this, as I don't have access to each index from NSIndexSet.
}
}
}
[[arrayEvents objectsAtIndexes:indexesOfObjects]
setValue:[arrayDates objectAtIndex:i] forKey:#"filter_date"];
isn't worked.
Reference, How to indicate index of NSIndexSet object? but doesn't help me.

You can try doing something like this:
NSMutableDictionary *dict=[[NSMutableDictionary alloc]initWithDictionary:[arrayEvents objectAtIndex:indexPath.row]];
[dict setValue:[arrayDates objectAtIndex:i] forKey:#"filter_date"];
[arrayEvents replaceObjectAtIndex:indexPath.row withObject:dict];
or you can use
replaceObjectsAtIndexes:(NSIndexSet *) withObjects:(NSArray *)a
which ever suits your convenience

Related

When iterating an NSMutableArray, how to delete one object then insert multiple objects?

For example, if we have an instance of NSMutableArray, with 10 objects in it. When iterating over it, we found that we have to delete object a[2] and a[8], and then insert 3 objects at a[2] and 4 objects at a[8] continuously, how to do that at lowest time cost?
Any idea will be thankful!
Edit : As #trojanfoe pointed out, it's nice to add that you should never edit an array while iterating it. This is true for many collection classes in many different languages; not just NSMutableArray & Objective-C. Doing so can easily leads to out-of-bounds index.
For your question, let's do it in two iteration.
First, we want to save the indexes we want to delete, so we will iterate on the sourceArray.
NSMutableArray * indexesToRemove = [NSMutableArray array];
[sourceArray enumerateObjectsUsingBlock:^(NSNumber * obj, NSUInteger idx, BOOL * _Nonnull stop) {
if (obj.integerValue%2 == 1) {
// Insert at first position
[indexesToRemove insertObject:#(idx) atIndex:0];
}
}];
It's important to save the index in an array and not a set, since you want to insert objects later. Also, add the new items at the beginning of the array is important, so you will iterate from the biggest index to the smallest, and will not have to shift the indexes according to the previously added items.
Now, you can, in a new iteration (this time on the index array), delete the items and add the new ones according to the indexes you saved :
[indexesToRemove enumerateObjectsUsingBlock:^(NSNumber * obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSUInteger indexToRemove = obj.unsignedIntegerValue;
// Delete the item from the source array
[sourceArray removeObjectAtIndex:indexToRemove];
// Create the items you want to insert, do whatever you want in this method :]
NSArray * itemsToAdd = [self generateElementsToAddAtIndex:indexToRemove];
// Create the indexSet according to the start index and the number of objects you want to insert
NSIndexSet * indexSet = [NSMutableIndexSet indexSetWithIndexesInRange:NSMakeRange(indexToRemove, itemsToAdd.count)];
// Insert the objects
[sourceArray insertObjects:itemsToAdd atIndexes:indexSet];
}];
[myMutableArray replaceObjectAtIndex:2 withObject:"5"];
will it work ?
First you have to delete object then use below line of code to insert multiple objects:
NSMutableOrderedSet *orderedSet = [[NSMutableOrderedSet alloc] init];
[orderedSet insertObjects:#[#"Eezy", #"Tutorials"] atIndexes:
[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, 2)]];
NSLog(#"Result: %#", orderedSet);
Refer below reference link:
http://ios.eezytutorials.com/nsmutableorderedset-by-example.php#.VwIJIhN95fg
For such a small array and so many operations, I consider replacing the array with a new one a good option - for performance as well as clarity.

UITableView with Year as section and Month as item GIVEN a set of NSManagedObject with NSDate property

I am working on a 'timeline' feature. In my core data I have a set of NSManagedObject (Object). each Object has a NSDate property (date).
No I want a UITableView as the timeline. Each year will be a section and each month will be an item. For example I have three Objects. Their dates are "Nov-2013", "Oct-2013", "Oct-2012". The table view should be like:
--2013--
Nov
Oct
--2012--
Oct
Because I want this table view to be synced with my database, I want to use fetched results controller. The naive approach I can think of now is to create a 'Date' object in my core data. 'Date' will have a many-to-one relationship with 'Object'. Every time I create an Object I will need to relate it with a Date object. Then I can simply create an NSFetchedResultsController by quering this Date object.
I am wondering if there is better solution than the above one? I don't really like the above solution because it requires creating a new object in core data..
Thanks!
-Erben
NSFetchedResultsController needs a persistent key for grouping... so if you want to 'sectionize by year, you have to create a year property in your mom.
well i suggest you to first fetch all results from core data and sort your array using comparatorBlock or selectorFunction, such that,
1)split the string into two at'-' using componentSeparator method.You will get a string array of two objects, one month and next year
2)Now give priority to year first by converting it to integer and matching.
After your array is sorted , now create and call function which store index positions of particular year into an array.
Based on those indexed year array , you can create table.
This is the logic that i want to follow. I will test and upload the code asap.
The final working code below.
NSArray *data = #[#"Nov-2013",#"Oct-2013",#"Oct-2012",#"Jan-2012",#"Feb-2012",#"Jan-2013"];
NSArray *monthArray = #[#"Jan",#"Feb",#"Mar",#"Apr",#"May",#"Jun",#"Jul",#"Aug",#"Sep",#"Oct",#"Nov",#"Dec"];
data = [data sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
NSArray *strArray1 = [(NSString *)obj1 componentsSeparatedByString:#"-"];
NSArray *strArray2 = [(NSString *)obj2 componentsSeparatedByString:#"-"];
NSComparisonResult result =NSOrderedSame;
NSInteger year1 = [((NSString*)strArray1[1]) integerValue];
NSInteger year2 = [((NSString*)strArray2[1]) integerValue];
if(year1>year2)
result = NSOrderedAscending;
else if (year1<year2)
result =NSOrderedDescending;
else{
NSInteger month1;
NSInteger month2;
for(month1=0;monthArray.count;month1++)
if([strArray1[0] isEqualToString:monthArray[month1]])
break;
for(month2=0;monthArray.count;month2++)
if([strArray2[0] isEqualToString:monthArray[month2]])
break;
if (month1>month2)
result=NSOrderedAscending;
else if (month1<month2)
result=NSOrderedDescending;
}
return result;
}];
NSLog(#"%#",data);
/*
(
"Nov-2013",
"Oct-2013",
"Jan-2013",
"Oct-2012",
"Feb-2012",
"Jan-2012"
)
*/
NSMutableArray *indexArray = [NSMutableArray array];
NSInteger lastIndex=0,i=0;
NSInteger lastYear =[((NSString *)[(NSString *)data[0] componentsSeparatedByString:#"-"][1]) integerValue];
for (i=1; i<data.count; i++) {
NSInteger year =[((NSString *)[(NSString *)data[i] componentsSeparatedByString:#"-"][1]) integerValue];
if(year!=lastYear){
NSDictionary *dic =#{#"year":[NSNumber numberWithInteger:lastYear],#"fromIndex":[NSNumber numberWithInteger:lastIndex],#"toIndex":[NSNumber numberWithInteger:i-1]};
[indexArray addObject:dic];
lastYear=year;
lastIndex=i;
}
}
NSDictionary *dic =#{#"year":[NSNumber numberWithInteger:lastYear],#"fromIndex":[NSNumber numberWithInteger:lastIndex],#"toIndex":[NSNumber numberWithInteger:i-1]};
[indexArray addObject:dic];
NSLog(#"%#",indexArray);
/*
(
{
fromIndex = 0;
toIndex = 2;
year = 2013;
},
{
fromIndex = 3;
toIndex = 5;
year = 2012;
}
)
*/
Use this arrayof dictionaries to feed data to table.
Hope this HELPS.

Finding a duplicate numbers in an array and then counting the number of duplicates

How would I take an array with long list of numbers that contains duplicates, so for instance:
NSArray *array = [NSArray arrayWithObjects:#"45", #"60", #"100",#"100", #"100", #"60"nil];
Just imagine that this is a HUGE list of random numbers. Now I'm sure that I have to use something like NSSet for this, but i'm not sure how to execute this. Also, once we identify the duplicates I'm guessing that I would then add those numbers to an array, and then call
[array count];
Any ideas?
NSCountedSet *set = [[NSCountedSet alloc] initWithArray:array];
int duplicates = 0;
for (id object in set) {
if ([set countForObject:object] > 1) {
duplicates++;
}
}
This will calculate how many elements have a duplicate.
A sidenote, that array contains a bunch of strings, no numbers...
Anyway, if the goal is to get just the count you could use the following single line to get it.
NSUInteger diff = [array count] - [[array valueForKeyPath:#"#distinctUnionOfObjects.self"] count];
This uses KVC (Key-Value Coding) to get all distinct objects (that is ones without a dupe) counts them and gets the difference from the original count.
NSCountedSet is perfect for what you want to do.
NSArray *array = [NSArray arrayWithObjects:#"45", #"60", #"100",#"100", #"100", #"60",nil];
NSCountedSet *countedSet = [NSCountedSet setWithArray:array];
__block NSUInteger totalNumberOfDuplicates = 0;
[countedSet enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {
NSUInteger duplicateCountForObject = [countedSet countForObject:obj];
if (duplicateCountForObject > 1)
totalNumberOfDuplicates += duplicateCountForObject;
NSLog(#"%# appears %ld times", obj, duplicateCountForObject);
}];
NSLog(#"Total number of duplicates is %ld", totalNumberOfDuplicates);
produces:
45 appears 1 times
60 appears 2 times
100 appears 3 times
Total number of duplicates is 5
Use filteredArrayUsingPredicate this use a predicate with your condition and return an array with the objects you need.
NSArray* arr=[[NSArray alloc] initWithObjects:#"10",#"11",#"10",#"2", nil];
NSLog(#"%d",[[arr filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"SELF == '2'"]] count]);

How to check if a NSArray has the values of another array

I have my array unique that is my main array and my array kind. I need to check that only 1 value of kind is present in the array unique. Then if there is more than 1 value of the array kind in unique I need to unset all values but the first one used in the array.
The further i got to achieve this is with the following code but I can not store the indexpath of the found object to do a later comparison. xcode says "bad receiver type nsinteger"
could anyone help me to achieve this?
kind = #[#"#Routine",#"#Exercise",#"#Username"];
NSMutableArray *uniqueKind = [NSMutableArray array];
for (NSString* obj in kind) {
if ( [unique containsObject:obj] ) {
NSInteger i = [unique indexOfObject:obj];
[uniqueKind addObject: [i intValue]];
}
}
An NSInteger is like an int, so you can't send it a message ([i intValue]). Also, you can't add an NSInteger to an array without making it an NSNumber or some other object type. You can do it like this:
NSInteger i = [unique indexOfObject:obj];
[uniqueKind addObject: [NSNumber numberWithInteger:i]];
Also (without understanding what you're doing) you might want to use an NSSet instead of an array. And you can combine a couple of calls:
NSUInteger i = [unique indexOfObject:obj];
if ( i != NSNotFound ) {
[uniqueKind addObject:[NSNumber numberWithInteger:i]];
}
I'm not sure if it would solve your problem, but have you considered using sets (or mutable variation) instead of arrays? They ensure uniqueness and they allow you to check for intersection/containment. See the NSSet class reference.
You have to add objects to the NSMutableArray, not the actual intValue. Try converting teh integer to a NSNumber first.
[uniqueKind addObject: [NSNumber numberWithInt:i]];
instead.
(edited )

Sort NSArray of NSStrings based on a super set of ordered Strings

I have an NSArray which contains some NSString objects. For example:
NSArray *objects = #[#"Stin",#"Foo",#"Ray",#"Space"];
Now I need to sort this array based on following order of Strings.
NSArray *sortOrder = #[#"John",#"Foo",#"Space",#"Star",#"Ray",#"Stin"];
So the answer should be
NSArray *sorted = #[#"Foo",#"Space",#"Ray",#"Stin"];
How can I achieve this?
ANSWER:
Based on Accepted answer of dasblinkenlight, I did following and it worked to charm.
NSMutableArray *objects = #[#"Star",#"Stin",#"Foo",#"Ray",#"Space",#"John"];
NSArray *sortOrder = #[#"John",#"Foo",#"Space",#"Star",#"Ray",#"Stin"];
[objects sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {
int index1 = [sortOrder indexOfObject:obj1];
int index2 = [sortOrder indexOfObject:obj2];
if (index1 > index2)
return NSOrderedDescending;
if (index1 < index2)
return NSOrderedAscending;
return NSOrderedSame;
}];
Create NSComparator that holds a reference to the superset array, and decides the relative order of strings by comparing the results of calling [superset indexOfObject:str] on both strings. Call sortedArrayUsingComparator: passing an instance of NSComparator to get the desired ordering.
dasblinkenlight's solution would work, but like most programming problems there are multiple ways to go about it. Here is one such alternative:
NSMutableArray *sorted = [NSMutableArray arrayWithCapacity:0];
[sortOrder enumerateObjectsUsingBlock:^(NSString *sortedString, NSUInteger idx, BOOL *stop) {
if ([objects containsObject:sortedString]) {
[sorted addObject:sortedString];
}
}];
The variable names correspond to the variable names used in the original question.
This works because the enumeration happens in order. Therefore, what takes place is, essentially, a cull of the objects that exist in both arrays in the order as specified by sortOrder.

Resources