MrSpoocy: Array in anonymen Hash über die Referenz löschen ?

Hi, also ich hab hier einen kleinen Code welcher an sich auch geht. Jedoch würde ich das gern optimieren und so weit es geht nur über die Referenz arbeiten.

my $public_chat = {};  
  
sub onFlood2 {  
	$public_chat->{'Manuel'} = [[time(), 'Flood ?', 1], [time()-8, 'Spam!', 4], [time()-20, 'Unknown', 1]];  
	$public_chat->{'Robert'} = [[time(), 'Bla Bla', 2], [time()-8, 'FLOOOD', 7], [time()-20, 'Lalala', 1]];  
  
	# Durchlaufe jeden Benuter  
	foreach (keys %$public_chat)  
	{  
		my $user_ref = $public_chat->{$_};  
  
		for (my $i=0; $i < @$user_ref; $i++)  
		{  
			if ($user_ref->[$i][2] > 3)  
			{  
				delete $user_ref->[$i];  
			}  
		}  
	}  
	print Dumper(\$public_chat);  
  
	######  
	#  
	# try to do it with ref !  
	$public_chat->{'Manuel'} = [[time(), 'Flood ?', 1], [time()-8, 'Spam!', 4], [time()-20, 'Unknown', 1]];  
	$public_chat->{'Robert'} = [[time(), 'Bla Bla', 2], [time()-8, 'FLOOOD', 7], [time()-20, 'Lalala', 1]];  
	foreach (keys %$public_chat)  
	{  
		my $user_ref = $public_chat->{$_};  
	  
		foreach my $data_ref (@$user_ref)  
		{  
			if ($data_ref->[2] > 3)  
			{  
				# Yes, its same about top "delete"  
				print "Same ?: ".Dumper(\$data_ref);  
				  
				# but how can i delete this from original ?  
				# not work  
				#delete $data_ref;  
				  
				#not work  
				#delete @$data_ref;  
				  
				# not work  
				#delete $data_ref->{};  
			}  
		}  
	}  
}  
  
&onFlood2();

Die 1te foreach schleife macht alles richtig, sie löscht Einträge welchen im Array an 3ter (2 Index) einen höheren wert als 3 hat. In der 2ten foreach bekomme ich zwar den richtigen Eintrag aber finde keine Möglichkeit diesen zu löschen (es muss löschen und nicht nur leeren sein).

Ich hab auch schon versucht das ganze mit "grep" zu verbessern aber lange beim gleichen Problem das ich die Ref nicht löschen kann.

über Ideen oder Lösungen würde ich mich freuen
mfg Spoocy

  1. Moin Moin!

    &onFlood2();

      
       ^-- Du weißt, was & in Perl 5 anderes macht als in Perl 4?  
      
    Willst Du das wirklich? Und wenn ja, warum?  
      
      
    Der Rest Deines Problems dürfte an deiner merkwürdigen Datenstruktur liegen, und an einem grundlegenden Mißverständnis, wie Daten strukutiert werden.  
      
    Perl hat keine Möglichkeit, zu einer Referenz herauszufinden, wo diese gespeichert ist, weil die wortwörtlich überall und nirgends gespeichert werden kann. Es gibt keinen Parent-Eintrag, demzufolge kann es keine Funktion geben, die einen Parent-Eintrag löscht.  
      
    Alexander
    
    -- 
    Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so".
    
    1. moin, moin,

      Der Rest Deines Problems dürfte an deiner merkwürdigen Datenstruktur liegen, und an einem grundlegenden Mißverständnis, wie Daten strukutiert werden.

      Du, ich habe heute wahnsinnig gute Laune, deswegen finde ich Deinen Kommentar überhaupt nicht merkwürdig ;)

      Hotti

      --
      Wenn Stukatöre Strukutieren wurden, müssten Diskutöre Dikutieren.
  2. hi,

    ok, Deine Datenstruktur sieht so aus:

      
    {  
      Manuel => [  
                  ["1302790201", "Flood ?", 1],  
                  ["1302790193", "Spam!", 4],  
                  ["1302790181", "Unknown", 1],  
                ],  
      Robert => [  
                  ["1302790201", "Bla Bla", 2],  
                  ["1302790193", "FLOOOD", 7],  
                  ["1302790181", "Lalala", 1],  
                ],  
    }  
      
    print $public_chat->{Robert}->[1]->[2]; # 7  
    
    

    Wenn Du da was löschen willst,
     $public_chat->{Robert}->[1]->[2]
                                   ^hierüber iterieren
                    ^hierüber iterieren

    Was sehen wir auf den ersten Blick? Der erste Index ist immer [1] und eigentlich überflüssig. Es sei denn, Du hast da noch was vor ;)

    Löschen von Array-Elementen:

      
    delete $public_chat->{Robert}->[1]->[2];  
    
    

    und wech isser:

      
    {  
      Manuel => [  
                  ["1302791052", "Flood ?", 1],  
                  ["1302791044", "Spam!", 4],  
                  ["1302791032", "Unknown", 1],  
                ],  
      Robert => [  
                  ["1302791052", "Bla Bla", 2],  
                  ["1302791044", "FLOOOD"],  
                  ["1302791032", "Lalala", 1],  
                ],  
    }  
    
    

    Hotti

    1. hi again,

      $public_chat->{Robert}->[1]->[2]
                                     ^hierüber iterieren
                      ^hierüber iterieren

      Den Cast nicht vergessen! Sonst wird die Referenz nicht richtig aufgelöst:

        
         $public_chat->{Robert}->[1]    # is what!?  
         @{$public_chat->{Robert}->[1]} # is a array!  
        
      
      

      Also immer schön die Klammern setzen.

      Hotti

  3. hmm, was soll eigentlich gemacht werden?

      
    use strict;  
    use warnings;  
    use Data::Dump qw(dump);  
      
    my $public_chat = {};  
      
    $public_chat->{'Manuel'} = [[time(), 'Flood ?', 1], [time()-8, 'Spam!', 4], [time()-20, 'Unknown', 1]];  
    $public_chat->{'Robert'} = [[time(), 'Bla Bla', 2], [time()-8, 'FLOOOD', 7], [time()-20, 'Lalala', 1]];  
      
    foreach my $name(keys %{$public_chat}){  
    	my $i = 0;                            # wir brauchen den Index  
    	foreach my $x(@{$public_chat->{$name}}){  
    		if($x->[2] > 3){  
    			#delete $public_chat->{$name}->[$i]->[2];    # Löscht den Wert  
    			#delete $public_chat->{$name}->[$i];     # Löscht Array-Ref  
    			#$public_chat->{$name}->[$i] = [];     # Macht Array leer  
    			#delete $public_chat->{$name}; # Löscht den User, hier sinds alle  
    		}  
    		$i++;  
    	}	  
    }  
    print dump $public_chat;  
    
    

    Such Dir's Beste raus ;)

    1. Hi,

      erst mal mega Danke für deine Hilfe, scheinst dir ja wirklich Gedanken über meinen Post gemacht zu haben. Freut mich :)

      Ja also die Struktur ist bisschen falsch, viel mir jetzt auch auf. Hier jetzt die richtige Struktur und auch ein "schlechtes" script welches geht.

      my $spam_seconds = 10;  
      my $public_chat = {'MrSpoocy' => {  
      		'Ich Spame !' => [time,time-3,time-15,time-7,time-2],  
      		'Oder auch so' => [time, time-1,time-4]  
      	},  
      	'Robert' => {  
      		'Text ABC' => [time, time-16],  
      		'Other Text' => [time,time-2,time-15]  
      	}  
      };  
        
      sub onFlood {  
      	print Dumper(\$public_chat);  
        
      	# Als erstes lösche alte Einträge  
      	foreach my $key (keys %$public_chat)  
      	{  
      		foreach my $message (keys %{$public_chat->{$key}})  
      		{  
      			for (my $i=0; $i< @{$public_chat->{$key}->{$message}}; $i++)  
      			{  
      				my $ctime = $public_chat->{$key}->{$message}->[$i];  
      				if ($ctime < (time - $spam_seconds))  
      				{  
      					splice (@{$public_chat->{$key}->{$message}}, $i, 1);  
      				}  
        
      			}  
      		}  
      	}  
        
      	print Dumper(\$public_chat);	  
      return 1;  
      }  
      onFlood();
      

      Was soll das ganze ?
      Nun ja, es soll wie man glaub ich auch erkennen kann ein antiSpam/Flood Filter werden. Diese jedoch (kann) berücksichtigen das jeder Text an sich gezählt wird. Sodas ein User in 10sek mehr als 3 Verschiedene Texte schreiben kann jedoch nicht 3 mal das gleiche in 10sek. Dieser Teil des Script's löscht erst mal nur alte Einträge welche eh schon älter als 10sek sind und somit ignoriert werden können. Löschen tu ich sie damit der Hash nicht unendlich groß wird.

      Ein Fehler ist mir sowohl bei mir als auch bei dir aufgefallen, wenn man mit # delete ein Element aus einem Array löscht, dann wird dieser nur zu # undef und die anderen Elemente rücken nicht auf, und das hat später im gesamt Script negative Auswirkungen (z.b. # length()). Aus diesem Grund nehme ich nun # splice.

      Was mich aber wundert (muss ich eine Wissenslücke habe) warum @{$public->{} nicht das gleiche ist wie @$public->{}, magst du mir das vielleicht erklären ohne auf die perldoc zu verweisen ;) ?

      mfg Spoocy

      1. hi,

        Was mich aber wundert (muss ich eine Wissenslücke habe) warum @{$public->{} nicht das gleiche ist wie @$public->{}, magst du mir das vielleicht erklären ohne auf die perldoc zu verweisen ;) ?

        Es kann das Gleiche sein, muss aber nicht. Schau Dir am Besten mal das Beispiel an:

          
        use strict;  
        use warnings;  
        use Data::Dump qw(dump);  
          
        my $ref = {  
        	foo => {1 => 'eins'},  
        	bar => {2 => 'zwei'},  
        };  
          
        %{$ref->{foo}} = (); # { bar => { 2 => "zwei" }, foo => {} }  
        %$ref->{foo} = ();   # { bar => { 2 => "zwei" }, foo => undef }  
          
        print dump $ref;  
        
        

        Das gibt unterschiedliche Ergebnisse und hängt damit zusammen, wie Perl die Referenzen auflöst, das ist oben gut zu sehen, denke ich.

        Das Heimtückische daran ist, dass es nicht einmal eine Fehlermeldung gibt und der fehlende Cast erst viel später bemerkt wird.

        Hotti

    2. Mir ist aufgefallen das beim löschen durch "splice" ja die Anzahl der Elemente kleiner wird, und somit die for (my $i .. ) nicht mehr richtig arbeiten kann, darum hier eine Bug freie Version :P

      sub onFlood {  
        
      	print Dumper(\$public_chat);  
        
      	# Als erstes lösche alte Einträge  
      	foreach my $key (keys %$public_chat)  
      	{  
      		foreach my $message (keys %{$public_chat->{$key}})  
      		{  
      			my @newTimes = grep {$_ > (time - $spam_seconds); } @{$public_chat->{$key}->{$message}};  
      			if (@newTimes)  
      			{  
      				$public_chat->{$key}->{$message} = [@newTimes];  
      			}  
      			else  
      			{  
      				delete $public_chat->{$key}->{$message};  
      			}  
      		}  
      	}  
        
      	print Dumper(\$public_chat);  
      return 1;  
      }  
      onFlood();