creating new NSDictionaries programmatically - nsmutabledictionary

I want to create a set of new NSDictionaries with different names dependant upon input data. Is this possible? E.g.
for (int i = 0; i < [holidayDestination length]; i++) {
NSMutableDictionary *[NSString stringWithFormat:#"holidayDestination%d", i] = [[NSMutableDictionary alloc] init];
// Other code here...
}
Thanks in advance.

No, while Objective-C strives to be like Smalltalk, it's only a thin veneer over C, so you really can't do these kinds of cool "meta" tricks. What you can do instead is set up a mutable array and for each element at i create your mutable dictionary. Something like this:
NSMutableArray *holidayDestinations = [NSMutableArray new];
for (int i = 0; i < [holidayDestination length]; i++)
{
[holidayDestinations addObject:[NSMutableDictionary new]];
NSMutableDictionary *working = [holidayDestinations objectAtIndex:i];
// Add elements to "working"
}

Related

Strip down webpage to just text on iOS (Objective C)

My main goal here is to achieve an effect like Readability or Safari's Reader service where the main content of the webpage is converted to text. I don't actually want to display any images, just get all the webpage's important text. I am currently using some pretty long self-built code to parse the webpage for the s to find out what the heading may look like, and I am also parsing the s that I hope contain the majority of the page's content.
-(void)interpretAndDisplay {
NSURL *URL = [NSURL URLWithString:self.url];
NSData *data = [NSData dataWithContentsOfURL:URL];
NSString *html = [NSString stringWithUTF8String:[data bytes]];
//Getting the H1s
NSMutableArray *h1Full = [[NSMutableArray alloc] init];
h1Full = [self stringsBetweenString:#"<h1" andString:#">" andText:html];
if ([h1Full count] > 0) {
NSMutableArray *h1Content = [[NSMutableArray alloc] init];
h1Content = [self stringsBetweenString:[NSString stringWithFormat:#"<h1%#>",[h1Full firstObject]] andString:#"</h1>" andText:html];
NSMutableArray *h1Sanitize = [[NSMutableArray alloc] init];
h1Sanitize = [self stringsBetweenString:#"<" andString:#">" andText:html];
if ([h1Content count] > 0) {
NSString *finalTitle = [h1Content firstObject];
for (int i = 0; i < [h1Sanitize count]; i++) {
NSString *toRemove = [NSString stringWithFormat:#"<%#>",[h1Sanitize objectAtIndex:i]];
finalTitle = [finalTitle stringByReplacingOccurrencesOfString:toRemove withString:#""];
finalTitle = [finalTitle stringByReplacingOccurrencesOfString:#"\n" withString:#""];
}
finalTitle = [self sanitizeString:finalTitle];
[self.titleLabel setText:finalTitle];
}
}
//Now for the body!
NSMutableArray *pTag = [[NSMutableArray alloc] init];
pTag = [self stringsBetweenString:#"<p" andString:#">" andText:html];
if ([pTag count] > 0) {
NSMutableArray *pContent = [[NSMutableArray alloc] init];
pContent = [self stringsBetweenString:[NSString stringWithFormat:#"<p%#>",[pTag firstObject]] andString:#"</p>" andText:html];
NSMutableArray *pSanitize = [[NSMutableArray alloc] init];
pSanitize = [self stringsBetweenString:#"<" andString:#">" andText:html];
if ([pContent count] > 0) {
for (int i = 0; i < [pContent count]; i++) {
NSString *pToEdit = [pContent objectAtIndex:i];
for (int i = 0; i < [pSanitize count]; i++) {
NSString *toRemove = [NSString stringWithFormat:#"<%#>",[pSanitize objectAtIndex:i]];
pToEdit = [pToEdit stringByReplacingOccurrencesOfString:toRemove withString:#""];
}
[pContent replaceObjectAtIndex:i withObject:pToEdit];
}
for (int i = 0; i < [pContent count]; i++) {
NSString *pToEdit = [pContent objectAtIndex:i];
pToEdit = [pToEdit stringByReplacingOccurrencesOfString:#"\n" withString:#""];
[pContent replaceObjectAtIndex:i withObject:pToEdit];
}
NSString *finalBody = #"";
for (int i = 0; i < [pContent count]; i++) {
if ([finalBody isEqualToString:#""]) {
finalBody = [NSString stringWithFormat:#"%#",[pContent objectAtIndex:i]];
}
else {
finalBody = [NSString stringWithFormat:#"%#\n\n%#",finalBody,[pContent objectAtIndex:i]];
}
}
finalBody = [self sanitizeString:finalBody];
[self.textLabel setText:finalBody];
}
}
}
The above code extracts all of the elements just fine and sanitizes them with a method that I created, but the problem is that just analyzing the P tags sometimes completely fails to simplify the content, and analyzing all possible content tags could mess with the content's order and layout.
Is there a better way or some framework that converts all the text into a nice string?
EDIT
Searching around, I found a Boilerpipe project that can extract text extremely easily (https://github.com/k-bx/boilerpipe/wiki/QuickStart). It looks as easy as this: String text= ArticleExtractor.INSTANCE.getText(url);
Can I do this on Objective C?
Edit 2
It appears that there is a boilerpipe API, but this has limited requests. I am mostly looking for a user-side solution.
Reggie is not the most tolerant approach in my opinion.
I'd try to find an existent open source (i.e. https://github.com/Kerrick/readability-js) and use WebKit to inject JS into a web page once loaded.
After that you can inject another JS, extracting the processed content (using the appropriate class from the source)
Then, using JavaScriptCore you can pass div's content to Objective-C (JS offers many ways of doing that)

Display multiple markers on the Google Map in Objective C

How can I display multiple markers on the Google Map in iOS?
I used the following approach, but it didn't work.
for (int i = 0; i < [array count]; i++)
{
pointsToUse[i] = CLLocationCoordinate2DMake([[[[array objectAtIndex:0] componentsSeparatedByString:#","] objectAtIndex:0] floatValue],[[[[array objectAtIndex:0] componentsSeparatedByString:#","] objectAtIndex:1] floatValue]);
[_map animateToLocation:pointsToUse[i]];
GMSMarkerOptions *options = [[GMSMarkerOptions alloc] init];
options.position = pointsToUse[i];
[_map animateToLocation:pointsToUse[i]];
[_map addMarkerWithOptions:options];
}
You're using [array objectAtIndex:0] (in two places), when I think you should probably be using [array objectAtIndex:i]?
Also, you probably don't need the calls to animateToLocation?
I tried your code. This seems to work fine. Just delete the object at index 0 after passing it values to pointsToUse.
NSMutableArray *array = [NSMutableArray arrayWithObjects:#"12.981902,80.266333",#"12.982902,80.266363", nil];
CLLocationCoordinate2D pointsToUse[5];
for (int i = 0; i < [array Size]; i++)
{
pointsToUse[i] = CLLocationCoordinate2DMake([[[[array objectAtIndex:0] componentsSeparatedByString:#","] objectAtIndex:0] floatValue],[[[[array objectAtIndex:0] componentsSeparatedByString:#","] objectAtIndex:1] floatValue]);
[array removeObjectAtIndex:0];
GMSMarkerOptions *options = [[GMSMarkerOptions alloc] init];
options.position = pointsToUse[i];
[mapView_ animateToLocation:pointsToUse[i]];
[mapView_ addMarkerWithOptions:options];
}
Try this:
-(void)plotMutliplePinsonMap
{
mapView_ = [[GMSMapView alloc]initWithFrame:CGRectMake(0, 96, 320, 450)];
for(int i=0;i<[arrTobeShown count];i++)
{
double_lat = [[[arrTobeShown objectAtIndex:i]valueForKey:#"latitude"] doubleValue];
double_long = [[[arrTobeShown objectAtIndex:i]valueForKey:#"longitude"] doubleValue];
GMSMarker *mkr = [[GMSMarker alloc] init];
if (double_lat !=0 && double_long!=0)
{
[mkr setPosition:CLLocationCoordinate2DMake(double_lat, double_long)];
[mkr setTitle:[[arrTobeShown objectAtIndex:i] valueForKey:#"name"]];
[mkr setSnippet:[[arrTobeShown objectAtIndex:i] valueForKey:#"address"]];
[mkr setMap:mapView_];
GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:double_lat longitude:double_long zoom:5];
mapView_.camera=camera;
}
}
[self.view addSubview:mapView_];
[mapView_ setHidden:YES];
[self.view layoutIfNeeded];
}
Yes both of you are correct. According to your suggestions I changed the code and it works.
But the problem is, I set the zoom and the zoom is fixed. If the two locations are far, I can't see both locations on one screen ( i need to pinch to see both). How can I see both locations at the same time?
My code is shown below.
-(void) displayMapwithPositionfortheArray:(NSMutableArray*) array
{
CLLocationCoordinate2D firstPoint = CLLocationCoordinate2DMake([[[[array objectAtIndex:0] componentsSeparatedByString:#","] objectAtIndex:0] floatValue],[[[[array objectAtIndex:0] componentsSeparatedByString:#","] objectAtIndex:1] floatValue]);
GMSCameraPosition *currloc = [GMSCameraPosition cameraWithLatitude:firstPoint.latitude
longitude:firstPoint.longitude
zoom:8
bearing:0
viewingAngle:45];
_map = [GMSMapView mapWithFrame:CGRectZero camera:currloc];
_map.myLocationEnabled = YES;
_map.frame = CGRectMake(0, heightOffset, self.view.frame.size.width, self.view.frame.size.height - heightOffset);
[self.view addSubview:_map];
CLLocationCoordinate2D pointsToUse[[array count]];
for (int i = 0; i < [array count]; i++)
{
pointsToUse[i] = CLLocationCoordinate2DMake([[[[array objectAtIndex:i] componentsSeparatedByString:#","] objectAtIndex:0] floatValue],[[[[array objectAtIndex:i] componentsSeparatedByString:#","] objectAtIndex:1] floatValue]);
GMSMarkerOptions *options = [[GMSMarkerOptions alloc] init];
options.position = pointsToUse[i];
[_map addMarkerWithOptions:options];
}
}

How to set focus center based on the searched result of geocodeAddressString

I have only a couple of months experience on Objective-C.
I have a code to search a list of customer address, then show the annotation of these addresses. As the customers cover many regions, I would like to make map view's center and span adapt to the results.
Therefore, I planed to record all searched place marks and calculate an average latitude and longitude. Below is my code
self.mapView.mapType = MKMapTypeStandard;
for (Customer *customer in customers) {
CLGeocoder *geoCoder = [[CLGeocoder alloc] init];
NSString *customerAddress = [Customer getWholeAddressOfCustomer:customer];
NSString *color = #"";
if([customer.status isEqualToString:#"1"])
{
color = #"Green";
}else if ([customer.status isEqualToString:#"2"]) {
color = #"Yellow";
}else if ([customer.status isEqualToString:#"3"])
{
color = #"Red";
}else {
color = customer.status;
}
[geoCoder geocodeAddressString:customerAddress completionHandler:^(NSArray *placemarks, NSError *error)
{
[NSThread sleepForTimeInterval:0.25];
if (placemarks.count == 0)
{
NSLog(#"No place for customer %# was found",customerAddress);
}
CLPlacemark *placemark = [placemarks objectAtIndex:0];
if(placemark.location.coordinate.latitude != 0.000000 && placemark.location.coordinate.longitude != 0.000000)
{
CustomerAnnotation *annotation = [[CustomerAnnotation alloc]initWithCoordinate:placemark.location.coordinate andName:customer.name andNumber:customer.customerNo andColor:color];
[self.mapAnnotations addObject:annotation];
}
}];
}
CLLocationDegrees totalLatitude=0;
CLLocationDegrees totalLongitude = 0;
for (int i=0; i < [self.mapAnnotations count]; i++) {
totalLatitude += [(CustomerAnnotation *)[self.mapAnnotations objectAtIndex:i] coordinate].latitude;
totalLongitude += [(CustomerAnnotation *)[self.mapAnnotations objectAtIndex:i] coordinate].longitude;
[self.mapView addAnnotation:[self.mapAnnotations objectAtIndex:i]];
}
MKCoordinateRegion focusRegion;
focusRegion.center.latitude = totalLatitude/[self.mapAnnotations count];
focusRegion.center.longitude = totalLongitude/[self.mapAnnotations count];
focusRegion.span.latitudeDelta = 20; //will modify this parameter later to self adapt the map
focusRegion.span.longitudeDelta = 20;
[self.mapView setRegion:focusRegion animated:YES];
But the problem is the timing issue like This problem
The setRegion is executed prior to obtaining those place marks. In the solution raised of that link, I should call a method in the completion block. But this solution is not suitable to a multiple address issue as I need to add all place marks to my method before I setRegion.
Is there anyway to make a method be executed after obtaining all results from geocodeAddressString?
Thanks in advance.
Issue resolved.
Outside of the block, I used a counter to record the annotation added.
__block int customerCount = 0;
When this count equals to the whole number, auto focus the central area of all customer.
if (customerCount == [customers count]) {
//Set the focus
}

Windows Azure: Argument Exception was unhandled

I want my Azure application to create a blob and write a data into it. When I tried it I got this exception stating that
ArgumentException was unhandled
Stream was not writable
here is my code
var ms = new MemoryStream();
for (int k = 0; k < 10; k++)
{
using (StreamWriter sw = new StreamWriter(ms))
{
string val = k.ToString();
if (k + 1 != len)
val = val + " ";
sw.Write(val);
sw.Flush();
}
}
ms.Position = 0;
blob.UploadFromStream(ms);
My code is getting executed for k = 0. The exception is thrown when k = 1.
Can anyone tell me how to solve this exception
Moreover, Is this the correct procedure for writing onto the blob. If no, where am I went wrong and how to correct it.
My guess is that the Finalize method of StreamWriter closes the underlying stream (so next time through the loop, you can't write to that MemoryStream).
I think you can solve this by puting the "using (StreamWriter sw = new StreamWriter(ms))" block around the whole loop. It's presumably more efficient than creating a new StreamWriter each time anyway.
In any case, if you're just writing text, it might be better to do something like:
StringBuilder sb = new StringBuilder();
for (int k = 0; k < 10; k++)
{
sb.Append(k.ToString());
if (k + 1 != len) sb.Append(" ");
}
blob.UploadText(sb.ToString());
Or (for this particular use), get fancy. :-) (completely untested):
blob.UploadText(string.Join(" ", Enumerable.Range(0, 10).Select(k => k.ToString()).ToArray()));

How to filter a specific color from a bitmapData object (or byte-array)

I'm looking for an efficient way to filter a specific color from a bitmapData object in ActionScript 3. Currently I use a loop with readByte32(). This takes about a second to process which is unacceptable. I have been trying to get paletteMap() to work but so far haven't been able to grasp its API (any truly useful links? Google has failed me...).
Here's my current logic, which I want to improve:
var n:int = bitmapData.width;
for (var i:int = 0; i < n; i++) {
var m:int = bitmapData.height;
for (var j:int = 0; j < m; j++) {
var color:int = bitmapData.getPixel(i, j);
if (color == 0xCACACA) {
bitmapData.setPixel32(i, j, 0x00000000);
}
}
}
I can get slightly better performance from using Vectors but it's only marginally better...
var v:Vector.<uint> = bitmapData.getVector(bitmapData.rect);
var n:int = bitmapData.width * bitmapData.height;
for (var i:int = 0; i < n; i++) {
var color:uint = v[i];
v[i] = color == 0xFFCACACA ? 0x00000000 : color;
}
bitmapData.setVector(bitmapData.rect, v);
I really think there must be a better way to do this that only takes a few 100 milliseconds. If anyone can unlock the mysteries of bitmapData for me, you will be the new leader of my people.
PS I am using bitmapData.lock() and unlock(); I just didn't post the boilerplate stuff.
An easy way is using the threshold method. It's a bit cumbersome at first, but it's pretty fast (as fast as you'll get, I think)
This will change every red pixel (considering red only a pixel whose value is exactly 0xffff0000) to blue (0xff0000ff).
var colorToReplace:uint = 0xffff0000;
var newColor:uint = 0xff0000ff;
var maskToUse:uint = 0xffffffff;
var rect:Rectangle = new Rectangle(0,0,bitmapData.width,bitmapData.height);
var p:Point = new Point(0,0);
bitmapData.threshold(bitmapData, rect, p, "==", colorToReplace,
newColor, maskToUse, true);
Flash has an API to a shader like language called pixel bender that may be of use to you in this case. Here is a tutorial from adobe on how to apply a pixel bender filter to an image in flash.
Otherwise you could processes rows at a time. (Note a slight error in your code was to re-get the height on each iteration of width):
private var currentRow:Number = 0;
private var timer:Timer;
public function processImage(event:Event=null):void
{
var m:int = bitmapData.height;
for (var j:int = 0; j < m; j++)
{
if (bitmapData.getPixel(currentRow, j) == 0xCACACA)
{
bitmapData.setPixel32(currentRow, j, 0x00000000);
}
}
currentRow++;
if(currentRow < bitmapData.width)
{
timer = new Timer(1, 500);
timer.addEventListener(TimerEvent.COMPLETE, processImage);
timer.start();
}
}
The processing will take a bit longer but at least your display won't be blocked.