iOS: CHCSVParser & NSPredicate? - ios

I'm currently attempting to use CHCSVParser to parse through a CSV file containing over 1500 entries, and 8 rows. I've successfully managed to parse through the file, and what I get is an NSArray of NSArrays of NSStrings.
For example here's what I get:
Loading CSV from: (
(
Last,
First,
Middle,
Nickname,
Gender,
City,
Age,
Email
),
(
Doe,
John,
Awesome,
"JD",
M,
"San Francisco",
"20",
"john#john.doe"
),
How could I sort this into a Person object and filter through it using NSPredicate, like Mattt Thompson does here.
Here's how I initialize the parser:
//Prepare Roster
NSString *pathToFile = [[NSBundle mainBundle] pathForResource:#"myFile" ofType: #"csv"];
NSArray *myFile = [NSArray arrayWithContentsOfCSVFile:pathToFile options:CHCSVParserOptionsSanitizesFields];
NSLog(#"Loading CSV from: %#", myFile);
Here's what Mattt does in the article I linked, which I'd like to do with my code:
NSArray *firstNames = #[ #"Alice", #"Bob", #"Charlie", #"Quentin" ];
NSArray *lastNames = #[ #"Smith", #"Jones", #"Smith", #"Alberts" ];
NSArray *ages = #[ #24, #27, #33, #31 ];
NSMutableArray *people = [NSMutableArray array];
[firstNames enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
Person *person = [[Person alloc] init];
person.firstName = firstNames[idx];
person.lastName = lastNames[idx];
person.age = ages[idx];
[people addObject:person];
}];

First, define a suitable Person class:
#interface Person : NSObject
#property(copy, nonatomic) NSString *firstName;
#property(copy, nonatomic) NSString *lastName;
// ...
#property(nonatomic) int age;
// ...
#end
Then you can read your data into an array of Person objects by enumerating the
myFile array. Inside the block, row is the "sub-array" for a single row:
NSMutableArray *people = [NSMutableArray array];
[myFile enumerateObjectsUsingBlock:^(NSArray *row, NSUInteger idx, BOOL *stop) {
if (row > 0) { // Skip row # 0 (the header)
Person *person = [[Person alloc] init];
person.lastName = row[0];
person.firstName = row[1];
// ...
person.age = [row[6] intValue];
// ...
[people addObject:person];
}
}];
Now you can filter that array as shown in the tutorial:
NSPredicate *smithPredicate = [NSPredicate predicateWithFormat:#"lastName = %#", #"Smith"];
NSArray *filtered = [people filteredArrayUsingPredicate:smithPredicate];

Related

iOS - Get unique values from multiple arrays using the same order

I have 2 arrays:
Array one: (#"26", #"26", #"25", #"25", #"3", #"3", #"4", #"4")
Array two: (#"sticker", #"sticker", #"sticker", #"sticker", #"sticker", #"frame", #"frame", #"frame")
Edit
These 2 arrays are connected like this:
26 - sticker
26 - sticker
25 - sticker
25 - sticker
3 - frame
3 - frame
4 - frame
4 - frame
I'm getting unique value in array one and putting it in another array like this:
NSArray *uniqueArrayOne = [[NSSet setWithArray:arrayOne] allObjects];
So uniqueArrayOne looks like this:
(26, 25, 3, 4)
I want uniqueArrayTwo to also be arrange like how uniqueArrayOne was arranged. I want uniqueArrayTwo to look like this:
(sticker, sticker, frame, frame)
What do I do?
Edit
Here is another example:
Array one: (#"TONY", #"SANSA", #"BILL", #"BILL", #"STEVE", #"STEVE")
Array two: (#"STARK", #"STARK", #"GATES", #"GATES", #"JOBS", #"JOBS")
The results should be like this:
uniqueArrayOne :(TONY, SANSA, BILL, STEVE)
uniqueArrayTwo :(STARK, STARK, GATES, JOBS)
uniqueArrayTwo is arrange depending on uniqueArrayOne.
I would solve it this way
- (void)uniqueArrayFinder
{
NSArray *array1 = [[NSArray alloc] initWithObjects:
#"TONY", #"SANSA", #"BILL", #"BILL", #"STEVE", #"STEVE",nil];
NSArray *array2 = [[NSArray alloc] initWithObjects:
#"STARK", #"STARK", #"GATES", #"GATES", #"JOBS", #"JOBS",nil];
NSMutableArray *combinedArray = [[NSMutableArray alloc] init];
for (int i=0; i<[array1 count];i++)
{
NSString *arrayVal1 = [array1 objectAtIndex:i];
NSString *arrayVal2 = [array2 objectAtIndex:i];
NSString *combinedStr = [NSString stringWithFormat:#"%# %#", arrayVal1, arrayVal2];
[combinedArray addObject:combinedStr];
}
//this gives uniqe values
NSSet *uniqueEvents = [NSSet setWithArray:combinedArray];
[combinedArray removeAllObjects];
[combinedArray addObjectsFromArray:[uniqueEvents allObjects]];
NSLog(#"combinedArray: %# ...", combinedArray);
}
Output:
combinedArray: (
"STEVE JOBS",
"TONY STARK",
"SANSA STARK",
"BILL GATES"
) ...
You can achieve the desired result with the following,
NSArray *arr1 = #[#"26", #"26", #"25", #"25", #"3", #"3", #"4", #"4"];
NSArray *arr2 = #[#"sticker", #"sticker", #"sticker", #"sticker", #"sticker", #"frame", #"frame", #"frame"];
NSDictionary *dict = [NSDictionary dictionaryWithObjects:arr2
forKeys:arr1];
NSArray *distinctArr1 = [[NSOrderedSet orderedSetWithArray:arr1] array];
NSMutableArray *distinctArr2 = [NSMutableArray array];
for (NSString *num1 in distinctArr1) {
[distinctArr2 addObject:dict[num1]];
}
// Your distinctArr2 is sorted based on distinctArr1's indices
Try this one.
NSArray *arrayOne = #[#"TONY", #"SANSA", #"BILL", #"BILL", #"STEVE", #"STEVE"];
NSArray *arrayTwo = #[#"STARK", #"STARK", #"GATES", #"GATES", #"JOBS", #"JOBS"];
NSArray *uniqueArrayOne = [[NSSet setWithArray:arrayOne] allObjects];
NSMutableArray *uniqueArrayTwo = [NSMutableArray array];
[uniqueArrayOne enumerateObjectsUsingBlock:^(id _Nonnull obj1, NSUInteger idx, BOOL * _Nonnull stop) {
NSUInteger found = [arrayOne indexOfObjectPassingTest:^BOOL(id _Nonnull obj2, NSUInteger idx, BOOL * _Nonnull stop) {
return [(NSString *)obj2 isEqualToString:(NSString *)obj1];
}];
[uniqueArrayTwo addObject:[arrayTwo objectAtIndex:found]];
}];
NSLog(#"%#, %#", uniqueArrayOne, uniqueArrayTwo);

String contains any element of an Array [duplicate]

I know I can check if a string contains another string like this
NSString *string = #"hello bla bla";
if ([string rangeOfString:#"bla"].location == NSNotFound) {
NSLog(#"string does not contain bla");
} else {
NSLog(#"string contains bla!");
}
But what if I have an NSArray *arary = #[#"one",#"two", #"three", #"four"] and I wanted to check if a string contains either one of these without just loop or have a bunch of or's (|| ). So it would be something like this
if (array contains one or two or three or four) {
//do something
}
But if I have a longer array this becomes tedious so is there another way, without just looping through?
EDIT
I want to check if myArray has any of theses values in valuesArray
valuesArray =#[#"one",#"two", #"three", #"four"];
myArray = [#"I have one head", #"I have two feet", #"I have five fingers"]
OUTPUT
outputArray = #[#"I have one head", #"I have two feet"]
There you go:
NSArray* arrRet = [myArray filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id __nonnull evaluatedObject, NSDictionary<NSString *,id> * __nullable bindings) {
for(NSString* val in valuesArray) {
if ([evaluatedObject rangeOfString:val].location != NSNotFound)
return true;
}
return false;
}]];
arrRet contains exactly the two desired strings.
A little bit more magic later you have your code without writing a loop :P
NSArray* arrRet = [myArray filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary<NSString *,id> * bindings) {
BOOL __block match = false;
[valuesArray enumerateObjectsUsingBlock:^(id __nonnull obj, NSUInteger idx, BOOL * __nonnull stop) {
*stop = match = [evaluatedObject rangeOfString:obj].location != NSNotFound;
}];
return match;
}]];
You could use a NSCompoundPredicate
NSCompoundPredicate *predicate = [NSCompoundPredicate orPredicateWithSubpredicates:subPredicates];
Where your subPredicates must look like
(
SELF CONTAINS[c] "one",
SELF CONTAINS[c] "two",
SELF CONTAINS[c] "three",
SELF CONTAINS[c] "four"
)
To get there from
NSArray *array = #[#"one", #"two", #"three", #"four"]
You could use a for loop, but as you are opposed to that, let's cheat:
by using a category I each NSArray functional mapping, but instead of looping, I use enumerating
#interface NSArray (Map)
-(NSArray *) vs_map:(id(^)(id obj))mapper;
#end
#implementation NSArray (Map)
-(NSArray *)vs_map:(id (^)(id))mapper
{
NSMutableArray *mArray = [#[] mutableCopy];
[self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
id mapped = mapper(obj);
[mArray addObject:mapped];
}];
return [mArray copy];
}
#end
Now I can create the subPredicates like
NSArray *subPredicates = [arary vs_map:^id(NSString *obj) {
return [NSPredicate predicateWithFormat:#"SELF contains[c] %#", obj];
}];
and create the compound predicate like
NSCompoundPredicate *predicate = [NSCompoundPredicate orPredicateWithSubpredicates:subPredicates];
and use it
BOOL doesContain = [predicate evaluateWithObject:string];
et voilĂ : No (obvious) looping, while there is one hidden in the enumeration and probably in the predicate as-well.
Now with the changed question you basically ask for filtering. You can use the same predicate for that:
NSArray *testarray = #[#"I have one head", #"I have two feet", #"I have five fingers"];
NSArray *arary = #[#"one",#"two", #"three", #"four"];
NSArray *subPredicates = [arary vs_map:^id(NSString *obj) {
return [NSPredicate predicateWithFormat:#"SELF contains[c] %#", obj];
}];
NSCompoundPredicate *predicate = [NSCompoundPredicate orPredicateWithSubpredicates:subPredicates];
NSArray *results = [testarray filteredArrayUsingPredicate:predicate];
results now contains
(
I have one head,
I have two feet
)
the complete code
#import <Foundation/Foundation.h>
#interface NSArray (Map)
-(NSArray *) vs_map:(id(^)(id obj))mapper;
#end
#implementation NSArray (Map)
-(NSArray *)vs_map:(id (^)(id))mapper
{
NSMutableArray *mArray = [#[] mutableCopy];
[self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
id mapped = mapper(obj);
[mArray addObject:mapped];
}];
return [mArray copy];
}
#end
int main(int argc, const char * argv[]) {
#autoreleasepool {
NSArray *testarray = #[#"I have one head", #"I have two feet", #"I have five fingers"];
NSArray *arary = #[#"one",#"two", #"three", #"four"];
NSArray *subPredicates = [arary vs_map:^id(NSString *obj) {
return [NSPredicate predicateWithFormat:#"SELF contains[c] %#", obj];
}];
NSCompoundPredicate *predicate = [NSCompoundPredicate orPredicateWithSubpredicates:subPredicates];
NSArray *results = [testarray filteredArrayUsingPredicate:predicate];
}
return 0;
}
Besides my cheating the my other question, here an idea how really to avoid time costly looping: Use Set computation magic!
created a class 'Sentence', instantiate it with the strings to test
created a 'Word' class that takes a word to search for
overwrite both classes' isEqual: method to match for if a word is in the sentence (use sets there too!)
put those into an array.
from this array create a NS(*)Set object
put all word in a set
execute union on it.

How to sort a NSArray with another NSArray?

NSArray A = #[[[#"id":#"3"]], [[#"id":#"4"]] ,[[#"id":#"c"]],[[#"id":#"f"]]];
NSArray idArray = #[#"c", #"3", #"4",#"f"];
Just a example I assumed.
How can I sort A by its id with idArray?
That is, I want A to become:
NSArray A= #[[[#"id":#"c"]], [[#"id":#"3"]] ,[[#"id":#"4"]],[[#"id":#"f"]]];
Now, I want to ask for an algorithm to sort array A to get the desired result.
---I get my answer when I search in google:
NSArray *sorter = #[#"B", #"C", #"E"];
NSMutableArray *sortee = [#[
#[#"B", #"abc"],
#[#"E", #"pqr"],
#[#"C", #"xyz"]
] mutableCopy];
[sortee sortUsingComparator:^(id o1, id o2) {
NSString *s1 = [o1 objectAtIndex:0];
NSString *s2 = [o2 objectAtIndex:0];
NSInteger idx1 = [sorter indexOfObject:s1];
NSInteger idx2 = [sorter indexOfObject:s2];
return idx1 - idx2;
}];
If you want to compare both array you can use
NSArray *array1=#[#"3",#"4",#"c","f"];
NSArray *array2=#[#"c",#"3",#"4","f"];
array1=[array1 sortedArrayUsingSelector:#selector(compare:)];
array2=[array2 sortedArrayUsingSelector:#selector(compare:)];
if ([array1 isEqualToArray:array2]) {
NSLog(#"both are same");
}
else{
NSLog(#"both are differnt");
}
or If you want to get common elements from 2 array use
NSMutableSet* set1 = [NSMutableSet setWithArray:array1];
NSMutableSet* set2 = [NSMutableSet setWithArray:array2];
[set1 intersectSet:set2]; //this will give you only the obejcts that are in both sets
NSArray* result = [set1 allObjects];
This would be a better way to make a dictionary for A. And then sorting based on their specific values like IQ, Name etc.
NSArray A = #[[[#"id":#"3"]], [[#"id":#"4"]] ,[[#"id":#"c"]],[[#"id":#"f"]]];
NSArray idArray = #[#"c", #"3", #"4",#"f"];
NSMutableArray *array = [NSMutableArray array];
for (int id = 0;idx<[A count];id++) {
NSDictionary *dict = #{#"Name": A[id],#"IQ":idArray[id]};
[array addObject:dict];
}
NSSortDescriptor *descriptor = [NSSortDescriptor sortDescriptorWithKey:#"IQ" ascending:NO];
[array sortUsingDescriptors:#[descriptor]]

check for a key within an object within an array of objects [duplicate]

I have a NSArray of Contact objects, we can call it contacts. Contact is a superclass, FacebookGroup and Individual are subclasses of Contact. FacebookGroup has a property called individuals which is a set of Individual objects.
I also have a NSArray of NSString objects, we can call it userIDs.
What I want to do is create a new NSArray from the existing contacts array that match the userIDs in userIDs.
So if contacts has 3 Contact objects with userID 1,2 and 3. And my userIDs has a NSString object 3. Then I want the resulting array to contain Contact which equals userID 3.
Contact.h
Contact : NSObject
FacebookGroup.h
FacebookGroup : Contact
#property (nonatomic, strong) NSSet *individuals;
Individual.h
Individual : Contact
#property (nonatomic, strong) NSString *userID;
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"userId = %#", myContact.userId];
NSArray *filteredArray = [contacts filteredArrayUsingPredicate:predicate];
Is this what you are looking for?
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"userID IN %#", userIDs];
NSArray *filtered = [contacts filteredArrayUsingPredicate:predicate];
i'm expecting you want like this once see this one,
NSMutableArray *names = [NSMutableArray arrayWithObjects:#"one", #"two", #"three", #"four", nil];
NSMutableArray *ids = [NSMutableArray arrayWithObjects:#"1", #"2", #"2", #"3", nil];
NSMutableArray *array=[[NSMutableArray alloc]init];
for(int i=0;i<[ids count];i++){
if([[ids objectAtIndex:i] isEqualToString:#"2"])
[array addObject:[names objectAtIndex:i]];
}
NSLog(#"%#",array);
O/P:-
(
two,
three
)

How to search and combine elements inside NSDictionaries as arrays?

As a concrete example, suppose I have the following array of dictionaries, which share the same keys and value types:
[ {car: "Ferrari", make: "Italian", color: "red"},
{car: "Ferrari", make: "Italian", color: "yellow"},
{car: "Porsche", make: "German", color: "green"},
{car: "Porsche", make: "German", color: "silver"}
]
How would I be able to search only by a key (for example, by "car") through the dictionaries, and then combine the values that are different (in this case, the "color") as arrays, so that my resultant array is:
[ {car: "Ferrari", make: "Italian", color: ["red", "yellow"]},
{car: "Porsche", make: "German", color: ["green", "silver"]},
]
Thanks!
The following method will solve your problem:
- (NSArray*) combineValuesFromArray:(NSArray*) array byKey: (NSString*) key
{
NSMutableSet *differentValues = [NSMutableSet set];
for(NSDictionary *dict in array)
{
[differentValues addObject:dict[key]];
}
NSMutableArray *result = [NSMutableArray array];
for(NSString *value in differentValues)
{
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"%K == %#", key, value];
NSArray *filteredArray = [array filteredArrayUsingPredicate:predicate];
NSMutableDictionary *sets = [NSMutableDictionary new];
for(NSDictionary *dict in filteredArray)
{
for(NSString *dictKey in dict)
{
NSMutableSet *set = [sets[dictKey] mutableCopy];
if(set == nil) set = [NSMutableSet new];
[set addObject:dict[dictKey]];
sets[dictKey] = set;
}
}
NSMutableDictionary *newDict = [NSMutableDictionary new];
for(NSString *dictKey in sets)
{
newDict[dictKey] = ([sets[dictKey] count] == 1) ? [sets[dictKey] anyObject] : [sets[dictKey] copy];
}
[result addObject:[newDict copy]];
}
return [result copy];
}
I've used following code for testing:
NSString *car = #"car";
NSString *make = #"make";
NSString *color = #"color";
NSArray *array = #[ #{car: #"Ferrari", make: #"Italian", color: #"red"},
#{car: #"Ferrari", make: #"Italian", color: #"yellow"},
#{car: #"Porsche", make: #"German", color: #"green"},
#{car: #"Porsche", make: #"German", color: #"silver"}
];
NSLog(#"%#", [self combineValuesFromArray:array byKey:car]);
Here is my approach with a little better time complexity. The first function convertDataBasedOnKey will receive the value from which you may want to compare, as you said on the question you wanted to be searchable by a key. The second function getSharedDictionaryOfElements is just for creating the final dictionary with the values merged.
- (void)convertDataBasedOnKey:(NSString *)baseKey {
desiredFormatData = [[NSMutableArray alloc] init];
NSMutableString *filterString = [NSMutableString stringWithFormat:#"(%# ==",baseKey];
[filterString appendString:#" %#)"];
while (currentFormatData.count != 0) {
NSDictionary *aDictionary = [currentFormatData firstObject];
NSString *firstValue = [aDictionary objectForKey:baseKey];
NSArray *filtered = [currentFormatData filteredArrayUsingPredicate:
[NSPredicate predicateWithFormat:filterString, firstValue]];
NSDictionary *mergedDictionary = [self getSharedDictionaryOfElements:filtered sharingValueForKey:baseKey];
[desiredFormatData addObject:mergedDictionary];
[currentFormatData removeObjectsInArray:filtered];
}
NSLog(#"%#",desiredFormatData.description);
}
- (NSDictionary *)getSharedDictionaryOfElements:(NSArray *)anArray sharingValueForKey:(NSString *)aKey {
if (!anArray || anArray.count == 0) {
return nil;
}
// As all the elements in anArray share the same keys
NSMutableDictionary *resultDictionary = [[NSMutableDictionary alloc] init];
NSDictionary *modelDictionary = [anArray firstObject];
for (NSString *aKey in modelDictionary.allKeys) {
NSPredicate *filterByKey = [NSPredicate predicateWithValue:YES];
NSArray *resultArray = [[anArray valueForKey:aKey] filteredArrayUsingPredicate:filterByKey];
NSMutableArray *uniqueArray = [NSMutableArray arrayWithArray:[[NSSet setWithArray:resultArray] allObjects]];
[resultDictionary setObject:uniqueArray forKey:aKey];
}
return resultDictionary;
}
Example
Using this two functions to obtain what you asked will be something like this:
Having declared this two variables:
NSMutableArray *currentFormatData;
NSMutableArray *desiredFormatData;
Then filling the first one with the example data and making the operation based on a key "car":
currentFormatData = [NSMutableArray arrayWithArray:#[#{#"car":#"Ferrari", #"make":#"Italian", #"color":#"red"},
#{#"car":#"Ferrari", #"make":#"Italian", #"color":#"yellow"},
#{#"car":#"Porsche", #"make":#"German", #"color":#"green"},
#{#"car":#"Porsche", #"make":#"German", #"color":#"silver"}]];
[self convertDataBasedOnKey:#"car"];

Resources