पर्ल में किसी सरणी से मान को हटाने का सबसे अच्छा तरीका क्या है?


81

सरणी में बहुत सारे डेटा हैं और मुझे दो तत्वों को हटाने की आवश्यकता है।

नीचे कोड स्निपेट का उपयोग किया जा रहा है,

my @array = (1,2,3,4,5,5,6,5,4,9);
my $element_omitted = 5;
@array = grep { $_ != $element_omitted } @array;

3
यह तीन तत्वों को हटाता है।
मेडलॉक पेर्लमैन

शीर्ष सभी गैर-फ़ाइल आइटम फ़ॉर्म निर्देशिका सूची को हटाने की आवश्यकता है और "सरणी = grep {-f $ _} सरणी" मेरे लिए एक आकर्षण की तरह काम किया :)
taiko

जवाबों:


87

यदि आप पहले से ही उस तत्व के सूचकांक को जानते हैं जिसे आप हटाना चाहते हैं, तो ब्याह का उपयोग करें।

यदि आप खोज कर रहे हैं तो Grep काम करता है।

यदि आपको इनमें से बहुत कुछ करने की आवश्यकता है, तो आप अपने सरणी को क्रमबद्ध क्रम में रखेंगे तो आपको बेहतर प्रदर्शन मिलेगा, क्योंकि तब आप आवश्यक सूचकांक खोजने के लिए द्विआधारी खोज कर सकते हैं।

यदि यह आपके संदर्भ में समझ में आता है, तो आप हटाए गए रिकॉर्ड के लिए "जादू मूल्य" का उपयोग करने पर विचार कर सकते हैं, बल्कि फिर उन्हें हटाकर, डेटा आंदोलन पर सहेजने के लिए - हटाए गए तत्वों को अपरिभाषित करने के लिए सेट करें, उदाहरण के लिए। स्वाभाविक रूप से, इसके अपने मुद्दे हैं (यदि आपको "लाइव" तत्वों की संख्या जानने की आवश्यकता है, तो आपको इसे अलग से ट्रैक करने की आवश्यकता है, आदि), लेकिन आपके आवेदन के आधार पर परेशानी के लायक हो सकता है।

वास्तव में अब संपादित करें कि मैं दूसरा रूप लेता हूं - ऊपर grep कोड का उपयोग न करें। जिस तत्व को आप हटाना चाहते हैं, उसके सूचकांक को खोजने के लिए यह अधिक कुशल होगा, फिर इसे हटाने के लिए ब्याह का उपयोग करें (जिस कोड को आपने सभी गैर-मिलान परिणामों को जमा किया है ..)

my $index = 0;
$index++ until $arr[$index] eq 'foo';
splice(@arr, $index, 1);

यह पहली घटना को हटा देगा। सभी घटनाओं को हटाना बहुत समान है, सिवाय इसके कि आप सभी इंडेक्स को एक पास में लाना चाहेंगे:

my @del_indexes = grep { $arr[$_] eq 'foo' } 0..$#arr;

बाकी को पाठक के लिए एक उत्साह के रूप में छोड़ दिया जाता है - याद रखें कि जैसे ही आप इसे विभाजित करते हैं, सरणी बदल जाती है!

Edit2 जॉन सिराकुसा ने सही ढंग से कहा कि मेरे उदाहरण में एक बग था .. निश्चित, उस बारे में क्षमा करें।


13
यदि स्ट्रिंग नहीं मिली है, तो लूप अटक जाएगा, इसलिए मेरा $ इंडेक्स = 0 करें; मेरी $ गिनती = स्केलर @ इयर; $ इंडेक्स ++ तक $ गिरफ्तारी [$ इंडेक्स] eq 'फू' या $ इंडेक्स == $ गिनती; ब्याह (@arr, $ सूचकांक, 1);
अमीर

1
या my ($index) = grep { $arr[$_] eq 'foo' } 0..$#arr; if (defined $index) {splice(@arr, $index, 1); }- पहले मैच के लिए
चिंतनशील

13

ब्याह अनुक्रमणिका द्वारा सरणी तत्व (ओं) को हटा देगा। खोज और निकालने के लिए, अपने उदाहरण के रूप में, grep का उपयोग करें।


धन्यवाद spoulson। मैं उन सूचकांकों को दान करता हूं जिन्हें मुझे हटाना है और इसलिए मुझे grep का सहारा लेना पड़ा।
user21246

8

यह कुछ आप बहुत कुछ करने जा रहे हैं? यदि हां, तो आप एक अलग डेटा संरचना पर विचार करना चाह सकते हैं। Grep हर बार पूरे एरे को सर्च करने वाला है और एक बड़े एरे के लिए काफी महंगा हो सकता है। यदि गति एक मुद्दा है तो आप इसके बजाय एक हैश का उपयोग करने पर विचार कर सकते हैं।

आपके उदाहरण में, कुंजी संख्या होगी और मूल्य उस संख्या के तत्वों की गिनती होगी।


5

यदि आप बदलते हैं

my @del_indexes = grep { $arr[$_] eq 'foo' } 0..$#arr;

सेवा

my @del_indexes = reverse(grep { $arr[$_] eq 'foo' } 0..$#arr);

यह पहले सरणी के पीछे से तत्वों को हटाकर सरणी के पुनर्निधारण के मुद्दे से बचा जाता है। एक फॉरेस्ट लूप में एक स्प्लिस () डालना @arr को साफ करता है। अपेक्षाकृत सरल और पठनीय ...

foreach $item (@del_indexes) {
   splice (@arr,$item,1);
}

5

तुम splicing के बजाय सरणी टुकड़ा करने की क्रिया का उपयोग कर सकते हैं। ग्रेप उन सूचकांकों को वापस करने के लिए जिन्हें आप रखना चाहते हैं और स्लाइसिंग का उपयोग करें:

my @arr = ...;
my @indicesToKeep = grep { $arr[$_] ne 'foo' } 0..$#arr;
@arr = @arr[@indiciesToKeep];

मुझे इस दृष्टिकोण का तर्क और लालित्य विशेष रूप से पसंद है।
केव

हां वास्तव में, आप इसे एक-लाइनर की तरह भी लिख सकते हैं: @arr = @arr[grep ...]जो मुझे विशेष रूप से पसंद है। मुझे यकीन नहीं है कि यह कितना कुशल है, लेकिन मैं इसका उपयोग शुरू करने जा रहा हूं क्योंकि यह अन्य समाधानों से बदतर नहीं हो सकता है।
सोगर

3

मुझे लगता है कि आपका समाधान सबसे सरल और सबसे अधिक रखरखाव योग्य है।

शेष पोस्ट दस्तावेजों को तत्वों में बदलने की कठिनाई को दस्तावेज में बदल देता spliceहै। इस प्रकार, यह एक और अधिक पूर्ण उत्तर बना रहा है।

उन सूचियों को देखें जिनके माध्यम से आपको एक कुशल (यानी एक-पास) एल्गोरिथ्म के माध्यम से जाना जाता है ताकि सूची आइटम पर परीक्षणों को अनुक्रमित किया जा सके। और यह बिल्कुल भी सहज नहीं है।

sub array_remove ( \@& ) { 
    my ( $arr_ref, $test_block ) = @_;
    my $sp_start  = 0;
    my $sp_len    = 0;
    for ( my $inx = 0; $inx <= $#$arr_ref; $inx++ ) {
        local $_ = $arr_ref->[$inx];
        next unless $test_block->( $_ );
        if ( $sp_len > 0 && $inx > $sp_start + $sp_len ) {
            splice( @$arr_ref, $sp_start, $sp_len );
            $inx    = $inx - $sp_len;
            $sp_len = 0;
        }
        $sp_start = $inx if ++$sp_len == 1;
    }
    splice( @$arr_ref, $sp_start, $sp_len ) if $sp_len > 0;
    return;
}

2
एक साधारण "grep" समझने में बहुत आसान होने वाला है और उससे अधिक कुशल है।
रैंडल श्वार्ट्ज

5
किसी ने मेरी टिप्पणी को हटा दिया कि आपने स्पष्ट रूप से पाठ नहीं पढ़ा है।
Axeman

2

मैं उपयोग करता हूं:

delete $array[$index];

Perldoc हटाएँ


9
ऐरे वैल्यू पर डिलीट होने की संभावना है (अपने डॉक्टर को देखें)
e2-e4

3
यह सिर्फ उस सरणी इंडेक्स में संग्रहीत मान को हटाता है। कम से कम मेरे संस्करण में perl, (5.14)
मुर्गा

यह वास्तव में आप क्या सोचते हैं हटा नहीं है। यह केवल मूल्य को हटाता है, जिससे यह बनता है undef। इसके अलावा, रिंगो द्वारा लिंक किए गए डॉक से: "चेतावनी: सरणी मूल्यों पर कॉलिंग को जोर से हतोत्साहित किया जाता है। पर्ल व्यू तत्वों के अस्तित्व को हटाने या जांचने की धारणा वैचारिक रूप से सुसंगत नहीं है, और आश्चर्यजनक व्यवहार को जन्म दे सकती है।" (डॉक्टर में पिछले पैराग्राफ में सभी विवरण हैं)।
शिवलिंग

2

सरणी के 'कुछ' की सभी घटनाओं को हटा दें।

SquareCog के जवाब के आधार पर:

my @arr = ('1','2','3','4','3','2', '3','4','3');
my @dix = grep { $arr[$_] eq '4' } 0..$#arr;
my $o = 0;
for (@dix) {
    splice(@arr, $_-$o, 1);
    $o++;
}
print join("\n", @arr);

हर बार जब हम @arrइंडेक्स को हटाते हैं, तो हटाने का अगला सही इंडेक्स होगा $_-current_loop_step


2

आप गैर-कैप्चरिंग समूह और हटाने के लिए मदों की एक पाइप डेलिम सूची का उपयोग कर सकते हैं।


perl -le '@ar=(1 .. 20);@x=(8,10,3,17);$x=join("|",@x);@ar=grep{!/^(?:$x)$/o} @ar;print "@ar"'

2

मुझे जो सबसे अच्छा मिला वह "अनडेफ" और "ग्रीप" का संयोजन था:

foreach $index ( @list_of_indexes_to_be_skiped ) {
      undef($array[$index]);
}
@array = grep { defined($_) } @array;

यह चाल है! फेडरिको


अपरिभाषित तत्व मान को शून्य करने के लिए सेट करें। कुल तत्व (आकार) अभी भी समान हैं।
बोण्टावी होम

1
@BoontaweeHome, grepअंत में फिर उन्हें हटा देता है।
Deanna

1

बस यह सुनिश्चित करने के लिए कि मेरे पास grep और मानचित्र समाधान हैं, पहले मिलान किए गए तत्वों की अनुक्रमणिका (जिन्हें निकालना है) की खोज करना और फिर सीधे अनुक्रमणिका की खोज किए बिना grep द्वारा तत्वों को निकालना। मुझे प्रतीत होता है कि सैम द्वारा प्रस्तावित पहला समाधान जब उनका सवाल पूछा गया था तो वह पहले से ही सबसे तेज था।

    use Benchmark;
    my @A=qw(A B C A D E A F G H A I J K L A M N);
    my @M1; my @G; my @M2;
    my @Ashrunk;
    timethese( 1000000, {
      'map1' => sub {
          my $i=0;
          @M1 = map { $i++; $_ eq 'A' ? $i-1 : ();} @A;
      },
      'map2' => sub {
          my $i=0;
          @M2 = map { $A[$_] eq 'A' ? $_ : () ;} 0..$#A;
      },
      'grep' => sub {
          @G = grep { $A[$_] eq 'A' } 0..$#A;
      },
      'grem' => sub {
          @Ashrunk = grep { $_ ne 'A' } @A;
      },
    });

परिणाम है:

Benchmark: timing 1000000 iterations of grem, grep, map1, map2...
  grem:  4 wallclock secs ( 3.37 usr +  0.00 sys =  3.37 CPU) @ 296823.98/s (n=1000000)
  grep:  3 wallclock secs ( 2.95 usr +  0.00 sys =  2.95 CPU) @ 339213.03/s (n=1000000)
  map1:  4 wallclock secs ( 4.01 usr +  0.00 sys =  4.01 CPU) @ 249438.76/s (n=1000000)
  map2:  2 wallclock secs ( 3.67 usr +  0.00 sys =  3.67 CPU) @ 272702.48/s (n=1000000)
M1 = 0 3 6 10 15
M2 = 0 3 6 10 15
G = 0 3 6 10 15
Ashrunk = B C D E F G H I J K L M N

जैसा कि बीते हुए समय के अनुसार दिखाया गया है, किसी भी प्रकार के grep या मानचित्र परिभाषित इंडेक्स का उपयोग करके हटाए गए फ़ंक्शन को लागू करने का प्रयास करना बेकार है। सीधे सीधे हटाओ।

परीक्षण करने से पहले मैं सोच रहा था कि "मैप 1" सबसे कुशल होगा ... मुझे बेंचमार्क मेरे अनुमान पर अधिक बार भरोसा करना चाहिए। ;-)


0

यदि आप सरणी इंडेक्स जानते हैं, तो आप इसे हटा सकते हैं () । बंटवारे () और डिलीट () के बीच का अंतर यह है कि डिलीट () एरे के बाकी एलिमेंट्स को रिन्यू नहीं करता है।


मेरा वास्तव में रेनम्बर से तात्पर्य है, जो कि पेर्ल्डोक के अनुसार, ब्याह () करता है।
पॉवरलॉर्ड

0

इसी तरह का एक कोड मैंने एक बार स्ट्रिंग के एक सरणी से SB.1 से शुरू नहीं होने वाले तारों को हटाने के लिए लिखा था

my @adoSymbols=('SB.1000','RT.10000','PC.10000');
##Remove items from an array from backward
for(my $i=$#adoSymbols;$i>=0;$i--) {  
    unless ($adoSymbols[$i] =~ m/^SB\.1/) {splice(@adoSymbols,$i,1);}
}

0

आप बस यह कर सकते हैं:

my $input_Color = 'Green';
my @array = qw(Red Blue Green Yellow Black);
@array = grep {!/$input_Color/} @array;
print "@array";
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.