Sort an NSMutableArray - iphone

In my NSMutableArray I have these values which need to be sorted,
+0.00,
+0.00,
+0.00,
+0.10,
+0.21,
+3.04,
+3.45,
+3.56,
+4.87,
+5.01,
+5.05,
+5.40,
+6.07,
+6.57,
+6.71,
+7.54,
+7.58,
+8.11,
+8.94,
+9.42,
+9.49,
9.78,
-1.69,
-1.80,
-10.99,
-12.72,
-2.30,
-2.64,
-2.64,
-3.12,
-4.59,
-5.11,
-5.83,
-6.54,
-6.64
My code for sorting,
NSSortDescriptor * frequencyDescriptor = [[NSSortDescriptor alloc] initWithKey:ytd
ascending:YES];
id obj;
NSEnumerator * enumerator = [array objectEnumerator];
while ((obj = [enumerator nextObject])) NSLog(#"%#", obj);
NSArray * descriptors = [NSArray arrayWithObjects:frequencyDescriptor, nil];
NSArray * sortedArray = [array sortedArrayUsingDescriptors:descriptors];
NSLog(#"\nSorted ...");
enumerator = [sortedArray objectEnumerator];
while ((obj = [enumerator nextObject])) NSLog(#"%#", obj);
Output that I get,
2013-09-17 13:00:28.315 Pondo MF[1275:c07] +0.00
2013-09-17 13:00:28.316 Pondo MF[1275:c07] +0.00
2013-09-17 13:00:28.316 Pondo MF[1275:c07] +0.00
2013-09-17 13:00:28.316 Pondo MF[1275:c07] +0.10
2013-09-17 13:00:28.317 Pondo MF[1275:c07] +0.21
2013-09-17 13:00:28.317 Pondo MF[1275:c07] +0.34
2013-09-17 13:00:28.317 Pondo MF[1275:c07] +0.68
2013-09-17 13:00:28.317 Pondo MF[1275:c07] +1.35
2013-09-17 13:00:28.318 Pondo MF[1275:c07] +18.59
2013-09-17 13:00:28.318 Pondo MF[1275:c07] +3.04
2013-09-17 13:00:28.318 Pondo MF[1275:c07] +3.45
2013-09-17 13:00:28.319 Pondo MF[1275:c07] +3.56
2013-09-17 13:00:28.319 Pondo MF[1275:c07] +4.87
2013-09-17 13:00:28.319 Pondo MF[1275:c07] +5.01
2013-09-17 13:00:28.319 Pondo MF[1275:c07] +5.05
2013-09-17 13:00:28.320 Pondo MF[1275:c07] +5.40
2013-09-17 13:00:28.320 Pondo MF[1275:c07] +6.07
2013-09-17 13:00:28.320 Pondo MF[1275:c07] +6.57
2013-09-17 13:00:28.320 Pondo MF[1275:c07] +6.71
2013-09-17 13:00:28.321 Pondo MF[1275:c07] +7.54
2013-09-17 13:00:28.321 Pondo MF[1275:c07] +7.58
2013-09-17 13:00:28.321 Pondo MF[1275:c07] +8.11
2013-09-17 13:00:28.322 Pondo MF[1275:c07] +8.94
2013-09-17 13:00:28.322 Pondo MF[1275:c07] +9.42
2013-09-17 13:00:28.322 Pondo MF[1275:c07] +9.49
2013-09-17 13:00:28.322 Pondo MF[1275:c07] +9.78
2013-09-17 13:00:28.323 Pondo MF[1275:c07] -1.69
2013-09-17 13:00:28.323 Pondo MF[1275:c07] -1.80
2013-09-17 13:00:28.323 Pondo MF[1275:c07] -10.99
2013-09-17 13:00:28.323 Pondo MF[1275:c07] -12.72
2013-09-17 13:00:28.324 Pondo MF[1275:c07] -2.30
2013-09-17 13:00:28.324 Pondo MF[1275:c07] -2.64
2013-09-17 13:00:28.324 Pondo MF[1275:c07] -2.64
2013-09-17 13:00:28.324 Pondo MF[1275:c07] -3.12
2013-09-17 13:00:28.325 Pondo MF[1275:c07] -4.59
2013-09-17 13:00:28.325 Pondo MF[1275:c07] -5.11
2013-09-17 13:00:28.325 Pondo MF[1275:c07] -5.83
2013-09-17 13:00:28.326 Pondo MF[1275:c07] -6.54
2013-09-17 13:00:28.326 Pondo MF[1275:c07] -6.64
This is incorrect as sorted array should start with +18.59 as first value. Where is my code going wrong? Any pointers to fix it?

You can sort your array using the NSComparator block. It is very simple:
[array sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {
float f1 = [obj1 floatValue];
float f2 = [obj2 floatValue];
if(f1<f2)
return NSOrderedDescending;
else if(f1>f2)
return NSOrderedAscending;
else
return NSOrderedSame;
}];
In that case your array will be sorted in descending. If you want to sort it in ascending, just swap NSOrderedDescending and NSOrderedAscending in the block.

Assuming your numbers were strings, I created a function which shows how to convert those strings to NSNumbers and then sort using the "compare:" selector:
- (void) demonstrateSorting
{
NSArray * unsortedArray = #[ #"+0.00",#"+0.00",#"+0.00",#"+0.10",#"+0.21", \
#"+3.04",#"+3.45",#"+3.56",#"+4.87",#"+5.01",#"+5.05",#"+5.40",#"+6.07",#"+6.57",#"+6.71",\
#"+7.54",#"+7.58",#"+8.11",#"+8.94",#"+9.42",#"+9.49",#"9.78",#"-1.69",#"-1.80",\
#"-10.99",#"-12.72",#"-2.30",#"-2.64",#"-2.64",#"-3.12",#"-4.59",#"-5.11",\
#"-5.83",#"-6.54",#"-6.64"];
NSMutableArray * arrayOfNumbers = [[NSMutableArray alloc] initWithCapacity: [unsortedArray count]];
for(NSString * someNumberString in unsortedArray)
{
NSNumber * number = [NSNumber numberWithFloat:[someNumberString floatValue]];
[arrayOfNumbers addObject: number];
}
id obj;
NSEnumerator * enumerator = [arrayOfNumbers objectEnumerator];
while ((obj = [enumerator nextObject])) NSLog(#"%#", obj);
NSArray * sortedArray =
[arrayOfNumbers sortedArrayUsingSelector:#selector(compare:)];
NSLog(#"\nSorted ...");
enumerator = [sortedArray objectEnumerator];
while ((obj = [enumerator nextObject])) NSLog(#"%#", obj);
}

Related

Perl + Tk: pass a (Scrolled/Listbox) widget reference to a subroutine

I have a problem passing Listbox references from a subroutine to another subroutine. What I want to do in the end is: upon selection in BoxA find out what the value of the selected item is and select someting in boxB based on that value.
#!/usr/bin/perl -w
use Tk;
use strict;
use warnings;
my $mw = MainWindow -> new();
my #arr = qw(1 2 3 4 5);
my $button = $mw -> Button (-text=>"Push me", -command => \&buttonCall) -> pack;
sub callee{
my $boxARef = $_[0];
my $boxBRef = $_[1];
my $index = $boxARef -> curselection();
$boxBRef -> selectionSet($index);
}
sub buttonCall{
my $boxA = $mw -> Listbox(-exportselection=>0, -selectmode => 'browse') -> pack;
$boxA -> insert('end', #arr);
my $boxB = $mw -> Listbox(-exportselection=>0, -selectmode => 'multiple') -> pack;
$boxB -> insert('end', #arr);
$boxA -> bind ('<<ListboxSelect>>' => [\&callee,\$boxA,\$boxB] );
}
MainLoop;
Executing the code results in:
Tk::Error: Can't call method "selectionSet" on unblessed reference at ./stack-test.pl line 14.
<<ListboxSelect>>
(command bound to event)
I am quite new to Perl and will appreciate any help with references.
There are two problems:
If you use the [coderef, arg, arg...] form to specify a Tk callback, the coderef will always be called with the widget that triggered the event passed as first argument. So that is your $_[0]. The two args you want to pass will end up in $_[1]and $_[2].
Your variables $boxA and $boxB are references already, but you pass them with an additional \ operator. That means you would have to dereference them in your callback sub before trying to call Tk methods on them. - Or drop the \ in the callback definition:
use Tk;
use strict;
use warnings;
my $mw = MainWindow -> new();
my #arr = qw(1 2 3 4 5);
my $button = $mw -> Button (-text=>"Push me", -command => \&buttonCall) -> pack;
sub callee{
my $boxARef = $_[1];
my $boxBRef = $_[2];
my $index = $boxARef -> curselection();
$boxBRef -> selectionSet($index);
}
sub buttonCall{
my $boxA = $mw -> Listbox(-exportselection=>0, -selectmode => 'browse') -> pack;
$boxA -> insert('end', #arr);
my $boxB = $mw -> Listbox(-exportselection=>0, -selectmode => 'multiple') -> pack;
$boxB -> insert('end', #arr);
$boxA -> bind ('<<ListboxSelect>>' => [\&callee,$boxA,$boxB] );
}
MainLoop;
It seems to be something strange going on with the references to the boxes created in buttonCall(). The following works for me:
use strict;
use warnings;
use Tk;
my $mw = MainWindow -> new();
my #arr = qw(1 2 3 4 5);
my $boxA;
my $boxB;
my $button = $mw -> Button (
-text=>"Push me",
-command => sub { buttonCall( \$boxA, \$boxB ) }
) -> pack;
sub callee{
my ( $boxARef, $boxBRef ) = #_;
my $index = $$boxARef -> curselection();
$$boxBRef -> selectionSet($index);
}
sub buttonCall{
my ( $boxARef, $boxBRef ) = #_;
if ( !defined $$boxARef ) {
$$boxARef = $mw -> Listbox(-exportselection=>0, -selectmode => 'browse') -> pack;
$$boxARef -> insert('end', #arr);
$$boxBRef = $mw -> Listbox(
-exportselection=>0, -selectmode => 'multiple'
) -> pack;
$$boxBRef -> insert('end', #arr);
$$boxARef -> bind ('<<ListboxSelect>>' => sub { callee( $boxARef, $boxBRef) });
}
}
MainLoop;

How to get multi param with Dancer2?

I'm trying to get array in param, everything works well with single data, but not for array...
client-side (list of parameters sent from the browser):
list[1] null
list[2] 2
list[3] 10
list[4] null
server-side:
any ['get','post'] => '/save_list' => sub {
my $items = param ('list');
#result = null, mb is only the first element
#my $items = param ('list[]');
#result = null
#my #items = param ('list[]');
#result = empty
#my #items = param ('list');
#result = empty
};
Where Am I Wrong?
My version Dancer2-0.155004
The DSL keyword parameters will return a Hash::MultiValue object that you can use to access those in Dancer2.
any ['get','post'] => '/save_list' => sub {
my #items = parameters->get_all('list');
foreach my $item ( #items ) {
do_stuff($item);
}
};

getting the sort keys from a hash

This is the data dumper of \%spec_hash.
It is sorted by group which is a national exchange - and symbol.
foohost:~/walt $ vi /tmp/footoo
$VAR1 = {
'ARCX' => {
'IACI' => 1,
'MCHP' => 1,
},
'AMXO' => {
'YUM' => 1,
'SYK' => 1,
},
'XISX' => {
'FCEL' => 1,
'GPS' => 1,
}
};
I was trying to sort by keys these two hashes but cannot. For debugging purposes I really want to see what is getting pumped out of these hashes
foreach my $exch (sort keys %spec_hash) {
foreach my $exch (sort keys %{$spec_hash{$exch}}) {
If I comment out the dumper and try a regular sort :
#print Dumper(\%spec_hash) ;
foreach my $exch (sort keys %spec_hash) {
#foreach my $exch (sort keys %{$spec_hash{$exch}}) {
print "key: $exch, value: $spec_hash{$exch}\n"
}
this i what I get :
key: AMXO, value: HASH(0x9cc88a4)
key: ARCX, value: HASH(0x9cd6f1c)
key: XISX, value: HASH(0x9cbd5f0)
and trying to print this prints nothing at all :
foreach my $exch (sort keys %{$spec_hash{$exch}}) {
print "key: $exch, value: $spec_hash{$exch}\n"
}
If I understand correctly,
for my $exch (sort keys %spec_hash) {
for my $sym (sort keys %{ $spec_hash{$exch} }) {
print "Exchange: $exch, Symbol: $sym\n";
}
}
You want to loop over every symbol, but they are grouped by exchange, so you must first loop over the exchanges.
Data::Dumper doesn't sort its output by default.
Try adding $Data::Dumper::Sortkeys = 1; to your script.
use strict;
use warnings;
use Data::Dumper;
my %hash = (
'ARCX' => { 'IACI' => 1, 'MCHP' => 1, },
'AMXO' => { 'YUM' => 1, 'SYK' => 1, },
'XISX' => { 'FCEL' => 1, 'GPS' => 1, },
);
print do {
local $Data::Dumper::Sortkeys = 1;
Dumper \%hash;
};
Outputs:
$VAR1 = {
'AMXO' => {
'SYK' => 1,
'YUM' => 1
},
'ARCX' => {
'IACI' => 1,
'MCHP' => 1
},
'XISX' => {
'FCEL' => 1,
'GPS' => 1
}
};
Note: This can be modified to include a sort subroutine that you define

Converting HoA to HoH with counting

Have this code:
use 5.020;
use warnings;
use Data::Dumper;
my %h = (
k1 => [qw(aa1 aa2 aa1)],
k2 => [qw(ab1 ab2 ab3)],
k3 => [qw(ac1 ac1 ac1)],
);
my %h2;
for my $k (keys %h) {
$h2{$k}{$_}++ for (#{$h{$k}});
}
say Dumper \%h2;
produces:
$VAR1 = {
'k1' => {
'aa2' => 1,
'aa1' => 2
},
'k3' => {
'ac1' => 3
},
'k2' => {
'ab1' => 1,
'ab3' => 1,
'ab2' => 1
}
};
Is possible to write the above code with "another way"? (e.g. simpler or more compact)?
Honestly, I don't like the number of times $h2{$k} is evaluated.
my %h2;
for my $k (keys %h) {
my $src = $h{$k};
my $dst = $h2{$k} = {};
++$dst->{$_} for #$src;
}
A subroutine can help make the intent more obvious. Maybe.
sub counts { my %c; ++$c{$_} for #_; \%c }
$h2{$_} = counts(#{ $h{$_} }) for keys %h;
That can be simplified if you do the change in-place.
sub counts { my %c; ++$c{$_} for #_; \%c }
$_ = counts(#$_) for values %h;

perl passing array of hashes to a sub

I'm passing an array by reference to a sub function(\#charts);
the array charts contains hash array at i=0 and a string at i=1 and so on (hash-string..)
I want to store the keys of hashes in #x and the values in #y as shown in the below function.But I'm getting an infinite loop;it keeps printing ...
sub function{
print Dumper #_;
for ($i=0;$i<scalar #{$_[0]} ;$i+2)
{
#data= #{$_[$i]};
$title=$_[$i+1];
%hash =%{$data[$i]};
# print Dumper \%hash;
foreach my $key (sort { $a <=> $b} keys %hash) {
push (#x,$key);
push (#y,$hash{$key});
}
print Dumper #x;
}}
Output:
$VAR1 = [
{
'84' => 2,
'11' => 2,
'53' => 2,
'3' => 2,
'-46' => 2,
'14' => 2,
'-7' => 2,
'47' => 2,
'-10' => 2,
'0' => 2,
'72' => 2,
'-2' => 2
},
'1_-2_-2'
];
here is the #x infinite loop
...$VAR21817 = '-46';
$VAR21818 = '-10';
$VAR21819 = '-7';
$VAR21820 = '-2';
$VAR21821 = '0';
$VAR21822 = '3';
$VAR21823 = '11';
$VAR21824 = '14';
$VAR21825 = '47';
$VAR21826 = '53';
$VAR21827 = '72';
$VAR21828 = '84';
$VAR21829 = '-46';
$VAR21830 = '-10';
$VAR21831 = '-7';
$VAR21832 = '-2';
$VAR21833 = '0';
$VAR21834 = '3';
$VAR21835 = '11';
$VAR21836 = '14';
$VAR21837 = '47';
$VAR21838 = '53';
$VAR21839 = '72';
$VAR21840 = '84';
$VAR21841 = '-46';
$VAR21842 = '-10';
$VAR21843 = '-7';
$VAR21844 = '-2';
$VAR21845 = '0';
$VAR21846 = '3';
$VAR21847 = '11';
$VAR21848 = '14';
$VAR21849 = '47';
$VAR21850 = '53';
$VAR21851 = '72';
$VAR21852 = '84';
Where is the problem and how to fix ?
Thanks in advance
The infinite loop is because of this line
for ($i=0;$i<scalar #{$_[0]} ;$i+2)
# ^^^^---- here
This is the loop incrementor, and you are just feeding it a number, not incrementing $i. This part should of course be $i += 2.
This is a rather poor setup, though. I assume \#charts looks something like this
$VAR1 = [
{
'a' => 1,
'b' => 2
},
'foo',
{
'y' => 13,
'x' => 12
},
'bar'
];
What you should do is keep the sets together
$VAR1 = [
{
'title' => 'foo',
'data' => {
'a' => 1,
'b' => 2
}
},
{
'title' => 'bar',
'data' => {
'y' => 13,
'x' => 12
}
}
];
That way you can simply assign directly, and use a simple loop
for my $href (#_) {
my $title = $href->{title};
my %hash = %{ $href->{data} };
....
}
You should also add
use strict;
use warnings;
And declare your variables in the smallest scope possible, such as inside a loop in a subroutine.