मैं ऑब्जेक्टिव-सी में एक एनएसएरे को कैसे उलट सकता हूं?


356

मुझे अपना उल्टा करने की जरूरत है NSArray

उदहारण के लिए:

[1,2,3,4,5] बनना चाहिए: [5,4,3,2,1]

इसे हासिल करने का सबसे अच्छा तरीका क्या है?


1
यह देखने लायक भी है: http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/Collections/Articles/sortingFilteringArrays.html जो आपको रिवर्स ऑर्डर में सरणी को क्रमबद्ध करने का तरीका बताता है (जो आमतौर पर वही होता है) उदाहरण के लिए, आप एनएसडी # allKeys से प्राप्त एक सरणी का उपयोग करने के लिए कर रहे हैं, और आप iPhone, आदि पर यूआईटेबल के लिए समूहीकरण के लिए रिवर्स डेट / अल्फ़ा ऑर्डर चाहते हैं)।

जवाबों:


305

किसी सरणी की उलटी प्रतिलिपि प्राप्त करने के लिए, danielpunkass 'समाधान का उपयोग करके देखें reverseObjectEnumerator

एक परिवर्तनशील सरणी को उलटने के लिए, आप अपने कोड में निम्न श्रेणी जोड़ सकते हैं:

@implementation NSMutableArray (Reverse)

- (void)reverse {
    if ([self count] <= 1)
        return;
    NSUInteger i = 0;
    NSUInteger j = [self count] - 1;
    while (i < j) {
        [self exchangeObjectAtIndex:i
                  withObjectAtIndex:j];

        i++;
        j--;
    }
}

@end

15
फास्ट एन्यूमरेशन के बारे में बुरी चीजों में से एक यह है कि मेरे जैसे नए लोग रिवर्स ऑबजेक्टयूमरेटर जैसी ठंडी चीजों के बारे में नहीं सीखते हैं। यह करने के लिए बहुत साफ तरीका है।
ब्रेंट रॉयल-गॉर्डन

4
क्योंकि C ++ - पुनरावृत्तियों में एक और भी अधिक वाक्यविन्यास है, वे बदसूरत हैं।
जॉर्ज शॉर्ली

4
क्या आपको इसे वापस करने से पहले सरणी की नकल नहीं करनी चाहिए?
CIFilter

12
@ जॉर्ज: मैं इस पर आपसे असहमत हूं। अगर मुझे एक ऐसी विधि दिखाई देती है जो एक अपरिवर्तनीय वस्तु लौटाती है, तो मुझे उम्मीद है कि वास्तव में एक अपरिवर्तनीय वस्तु वापस आ जाएगी। यह एक अपरिवर्तनीय वस्तु को लौटाने के लिए प्रकट होता है, लेकिन वास्तविक रिटर्न एक उत्परिवर्तित वस्तु में प्रवेश करने के लिए एक खतरनाक अभ्यास है।
क्रिस्टीन

3
ReverseObjectEnumerator allObjects सुझाव देना उपयोगी है क्योंकि यह रिवर्स नहीं करता नहीं है परिवर्तनशील सरणी, यहां तक कि जोड़ने mutableCopy मदद नहीं है, क्योंकि अभी भी मूल सरणी उत्परिवर्तित नहीं है। Apple दस्तावेज़ जिन्हें अपरिवर्तनीयता को रन टाइम के लिए परीक्षण नहीं किया जाना चाहिए , लेकिन लौटे प्रकार के आधार पर माना जाता है, इसलिए इस मामले में एक NSMutableArray को वापस करना पूरी तरह से सही कोड है।
पीटर एन लुईस

1287

यदि आप बिल्ट-इन reverseObjectEnumeratorविधि NSArrayऔर allObjectsविधि का लाभ उठाते हैं, तो बहुत आसान उपाय है NSEnumerator:

NSArray* reversedArray = [[startArray reverseObjectEnumerator] allObjects];

allObjects प्रलेखित हैउन वस्तुओं के साथ एक सरणी लौटाने के रूप किया गया है nextObject, जिन्हें अभी तक क्रम में नहीं निकाला गया है :

इस सरणी में प्रगणित क्रम में प्रगणक की शेष सभी वस्तुएँ हैं ।


6
मैट विलियम्सन द्वारा यहां एक जवाब और नीचे दिया गया है जो एक टिप्पणी होनी चाहिए: danielpunkass के समाधान का उपयोग न करें। मैंने इसका इस्तेमाल करते हुए सोचा कि यह एक शानदार शॉर्टकट था, लेकिन अब मैंने सिर्फ 3 घंटे बिताए हैं यह जानने की कोशिश कर रहा हूं कि मेरा ए * एल्गोरिदम क्यों टूट गया। ऐसा इसलिए है क्योंकि यह गलत सेट लौटाता है!
जॉर्ज शॉली

1
Do गलत सेट ’से क्या मतलब है? एक सरणी जो रिवर्स ऑर्डर में नहीं है?
सिमो सलमिनन

31
मैं अब उस बग को पुन: पेश करने में सक्षम नहीं हूं। यह मेरी त्रुटि हो सकती थी। यह एक बहुत ही सुंदर समाधान है।
मैट विलियम्सन

3
दस्तावेज़ में अब आदेश की गारंटी है।
jscs

मुझे यकीन है कि यह अच्छा जवाब है, लेकिन यह म्यूटेबल ऑब्जेक्ट्स के लिए विफल हो जाएगा। क्योंकि NSEnumerator रीडऑनली ऑब्जेक्ट टाइप @property (आसानी से, कॉपी) NSArray <ObjectType> * allObjects प्रदान करता है;
अनुराग सोनी

49

कुछ बेंचमार्क

1. RevObjectEnumerator allObjects

यह सबसे तेज़ विधि है:

NSArray *anArray = @[@"aa", @"ab", @"ac", @"ad", @"ae", @"af", @"ag",
        @"ah", @"ai", @"aj", @"ak", @"al", @"am", @"an", @"ao", @"ap", @"aq", @"ar", @"as", @"at",
        @"au", @"av", @"aw", @"ax", @"ay", @"az", @"ba", @"bb", @"bc", @"bd", @"bf", @"bg", @"bh",
        @"bi", @"bj", @"bk", @"bl", @"bm", @"bn", @"bo", @"bp", @"bq", @"br", @"bs", @"bt", @"bu",
        @"bv", @"bw", @"bx", @"by", @"bz", @"ca", @"cb", @"cc", @"cd", @"ce", @"cf", @"cg", @"ch",
        @"ci", @"cj", @"ck", @"cl", @"cm", @"cn", @"co", @"cp", @"cq", @"cr", @"cs", @"ct", @"cu",
        @"cv", @"cw", @"cx", @"cy", @"cz"];

NSDate *methodStart = [NSDate date];

NSArray *reversed = [[anArray reverseObjectEnumerator] allObjects];

NSDate *methodFinish = [NSDate date];
NSTimeInterval executionTime = [methodFinish timeIntervalSinceDate:methodStart];
NSLog(@"executionTime = %f", executionTime);

परिणाम: executionTime = 0.000026

2. मैं एक रिवर्स ऑबजेक्टइंटररेटर पर काम कर रहा हूं

यह 1.5x और 2.5x के बीच धीमा है:

NSDate *methodStart = [NSDate date];
NSMutableArray *array = [NSMutableArray arrayWithCapacity:[anArray count]];
NSEnumerator *enumerator = [anArray reverseObjectEnumerator];
for (id element in enumerator) {
    [array addObject:element];
}
NSDate *methodFinish = [NSDate date];
NSTimeInterval executionTime = [methodFinish timeIntervalSinceDate:methodStart];
NSLog(@"executionTime = %f", executionTime);

परिणाम: executionTime = 0.000071

3. SortedArrayUsingComparator

यह 30x और 40x धीमी गति के बीच है (यहाँ कोई आश्चर्य नहीं):

NSDate *methodStart = [NSDate date];
NSArray *reversed = [anArray sortedArrayUsingComparator: ^(id obj1, id obj2) {
    return [anArray indexOfObject:obj1] < [anArray indexOfObject:obj2] ? NSOrderedDescending : NSOrderedAscending;
}];

NSDate *methodFinish = [NSDate date];
NSTimeInterval executionTime = [methodFinish timeIntervalSinceDate:methodStart];
NSLog(@"executionTime = %f", executionTime);

परिणाम: executionTime = 0.001100

तो [[anArray reverseObjectEnumerator] allObjects]यह स्पष्ट विजेता है जब यह गति और सहजता की बात करता है।


और मैं कल्पना कर सकता हूं कि बड़ी संख्या में वस्तुओं के लिए यह 30-40x से अधिक धीमा होगा। मुझे नहीं पता कि सॉर्ट एल्गोरिथ्म की जटिलता क्या है (सबसे अच्छा मामला ओ (एन * लोगन)?), लेकिन यह इंडेक्सऑफऑब्जेक्ट को भी बुला रहा है, जो संभवतः ओ (एन) है। इस प्रकार के साथ, यह ओ (एन ^ 2) हो सकता है। * लोगन) या कुछ और। अच्छा नहीं!
यूसुफ हम्फ्रे

1
एक बेंचमार्क का उपयोग करने के बारे में क्या enumerateObjectsWithOptions:NSEnumerationReverse?
ब्रैंड्सस्क्रिप्ट

2
मैंने बस एक बेंचमार्क का उपयोग किया था enumerateObjectsWithOptions:NSEnumerationReverse- शीर्ष एक 0.000072सेकंड में पूरा किया, सेकंड में ब्लॉक विधि 0.000009
ब्रैंड्सस्क्रिप्ट

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

21

दासबुट में सही दृष्टिकोण है, लेकिन उनके कोड में कुछ गलतियां हैं। यहाँ एक पूरी तरह से सामान्य कोड स्निपेट है जो किसी भी NSMutableArray को उल्टा कर देगा:

/* Algorithm: swap the object N elements from the top with the object N 
 * elements from the bottom. Integer division will wrap down, leaving 
 * the middle element untouched if count is odd.
 */
for(int i = 0; i < [array count] / 2; i++) {
    int j = [array count] - i - 1;

    [array exchangeObjectAtIndex:i withObjectAtIndex:j];
}

आप इसे C फ़ंक्शन में या बोनस अंक के लिए रैप कर सकते हैं, इसे NSMutableArray में जोड़ने के लिए श्रेणियों का उपयोग कर सकते हैं। (उस स्थिति में, 'ऐरे' 'स्व' बन जाएगा।) आप चाहें [array count]तो लूप से पहले एक वैरिएबल को असाइन करके और यदि आप चाहें तो उस वैरिएबल का उपयोग करके इसे ऑप्टिमाइज़ भी कर सकते हैं ।

यदि आपके पास केवल एक नियमित NSArray है, तो इसे रिवर्स करने का कोई तरीका नहीं है, क्योंकि NSArrays को संशोधित नहीं किया जा सकता है। लेकिन आप एक उलटी प्रतिलिपि बना सकते हैं:

NSMutableArray * copy = [NSMutableArray arrayWithCapacity:[array count]];

for(int i = 0; i < [array count]; i++) {
    [copy addObject:[array objectAtIndex:[array count] - i - 1]];
}

या एक लाइन में करने के लिए इस छोटी सी ट्रिक का उपयोग करें:

NSArray * copy = [[array reverseObjectEnumerator] allObjects];

यदि आप किसी सरणी पर पीछे की ओर लूप करना चाहते हैं, तो आप for/ a के inसाथ लूप का उपयोग कर सकते हैं [array reverseObjectEnumerator], लेकिन इसका उपयोग करने के लिए थोड़ा अधिक कुशल होने की संभावना है -enumerateObjectsWithOptions:usingBlock::

[array enumerateObjectsWithOptions:NSEnumerationReverse
                        usingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
    // This is your loop body. Use the object in obj here. 
    // If you need the index, it's in idx.
    // (This is the best feature of this method, IMHO.)
    // Instead of using 'continue', use 'return'.
    // Instead of using 'break', set '*stop = YES' and then 'return'.
    // Making the surrounding method/block return is tricky and probably
    // requires a '__block' variable.
    // (This is the worst feature of this method, IMHO.)
}];

( नोट : फाउंडेशन के पांच और वर्षों के अनुभव, एक नई उद्देश्य-सी सुविधा या दो, और टिप्पणियों से कुछ सुझाव के साथ 2014 में पर्याप्त रूप से अपडेट किया गया।)


क्या वह काम करता है? मुझे नहीं लगता कि NSMutableArray में कोई सेटऑब्जेक्ट: atIndex: मेथड है। हालांकि लूप के लिए सुझाए गए फिक्स के लिए धन्यवाद, और NSNumber के बजाय जेनेरिक आईडी का उपयोग करना।
हिमाद्री चौधरी

आप सही हैं, मैंने उसे पकड़ा जब मैंने कुछ अन्य उदाहरण पढ़े। अब तय हो गया।
ब्रेंट रॉयल-गॉर्डन

2
[सरणी गणना] आपको हर बार लूप कहा जाता है। यह बहुत महंगा है। यहां तक ​​कि एक फ़ंक्शन भी है जो दो ऑब्जेक्ट्स की स्थिति को बदलता है।
जॉर्ज शॉर्ली

8

ऊपर दिए गए अन्य उत्तरों की समीक्षा करने के बाद और मैट गैलाघेर की चर्चा यहाँ पा सकते हैं

मैं यह प्रस्ताव करता हूं:

NSMutableArray * reverseArray = [NSMutableArray arrayWithCapacity:[myArray count]]; 

for (id element in [myArray reverseObjectEnumerator]) {
    [reverseArray addObject:element];
}

जैसा कि मैट देखता है:

उपरोक्त मामले में, आप आश्चर्यचकित हो सकते हैं कि - [NSArray रिवर्स ऑबजेक्ट इन्नुमेटर] लूप के प्रत्येक पुनरावृत्ति पर चलाया जाएगा - संभवतः कोड को धीमा कर रहा है। <...>

कुछ ही समय बाद, वह इस प्रकार उत्तर देता है:

<...> "संग्रह" अभिव्यक्ति का मूल्यांकन केवल एक बार किया जाता है, जब लूप शुरू होता है। यह सबसे अच्छा मामला है, क्योंकि आप लूप के प्रति-प्रदर्शन प्रदर्शन को प्रभावित किए बिना एक सुरक्षित फ़ंक्शन को "संग्रह" अभिव्यक्ति में सुरक्षित रूप से डाल सकते हैं।


8

जॉर्ज शॉली की श्रेणियां बहुत अच्छी हैं। हालाँकि, NSMutableArray के लिए, सूचक के लिए NSUIntegers का उपयोग करने से सरणी रिक्त होने पर क्रैश हो जाता है। सही कोड है:

@implementation NSMutableArray (Reverse)

- (void)reverse {
    NSInteger i = 0;
    NSInteger j = [self count] - 1;
    while (i < j) {
        [self exchangeObjectAtIndex:i
                  withObjectAtIndex:j];

        i++;
        j--;
    }
}

@end

8

किसी सरणी को रिवर्स में एन्यूमरेट करने का सबसे कुशल तरीका:

का उपयोग करें enumerateObjectsWithOptions:NSEnumerationReverse usingBlock। इसके ऊपर @ जोहान्सफारेनक्रग के बेंचमार्क का उपयोग करते हुए, इसने 8x जल्दी पूरा किया [[array reverseObjectEnumerator] allObjects];:

NSDate *methodStart = [NSDate date];

[anArray enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
    //
}];

NSDate *methodFinish = [NSDate date];
NSTimeInterval executionTime = [methodFinish timeIntervalSinceDate:methodStart];
NSLog(@"executionTime = %f", executionTime);

7
NSMutableArray *objMyObject = [NSMutableArray arrayWithArray:[self reverseArray:objArrayToBeReversed]];

// Function reverseArray 
-(NSArray *) reverseArray : (NSArray *) myArray {   
    return [[myArray reverseObjectEnumerator] allObjects];
}

3

रिवर्स सरणी और इसके माध्यम से लूपिंग:

[[[startArray reverseObjectEnumerator] allObjects] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
    ...
}];

2

इसे अपडेट करने के लिए, स्विफ्ट में इसे आसानी से किया जा सकता है:

array.reverse()

1

मेरे लिए, क्या आपने विचार किया है कि सरणी को पहले स्थान पर कैसे आबाद किया गया था? मैं एक सरणी में MANY ऑब्जेक्ट्स को जोड़ने की प्रक्रिया में था, और किसी भी मौजूदा ऑब्जेक्ट को एक से ऊपर धकेलते हुए शुरुआत में हर एक को सम्मिलित करने का निर्णय लिया। इस मामले में, एक परिवर्तनशील सरणी की आवश्यकता होती है।

NSMutableArray *myMutableArray = [[NSMutableArray alloc] initWithCapacity:1];
[myMutableArray insertObject:aNewObject atIndex:0];

1

या स्काला-वे:

-(NSArray *)reverse
{
    if ( self.count < 2 )
        return self;
    else
        return [[self.tail reverse] concat:[NSArray arrayWithObject:self.head]];
}

-(id)head
{
    return self.firstObject;
}

-(NSArray *)tail
{
    if ( self.count > 1 )
        return [self subarrayWithRange:NSMakeRange(1, self.count - 1)];
    else
        return @[];
}

2
रिकर्सियन बड़े डेटा संरचनाओं, स्मृति-वार पर एक भयानक विचार है।
एरन गोल्डिन

यदि यह पूंछ पुनरावृत्ति के साथ संकलित करता है तो यह समस्या नहीं होनी चाहिए
simple_code

1

इसे करने का एक आसान तरीका है।

    NSArray *myArray = @[@"5",@"4",@"3",@"2",@"1"];
    NSMutableArray *myNewArray = [[NSMutableArray alloc] init]; //this object is going to be your new array with inverse order.
    for(int i=0; i<[myNewArray count]; i++){
        [myNewArray insertObject:[myNewArray objectAtIndex:i] atIndex:0];
    }
    //other way to do it
    for(NSString *eachValue in myArray){
        [myNewArray insertObject:eachValue atIndex:0];
    }

    //in both cases your new array will look like this
    NSLog(@"myNewArray: %@", myNewArray);
    //[@"1",@"2",@"3",@"4",@"5"]

आशा है कि ये आपकी मदद करेगा।


0

मैं किसी भी विधि से निर्मित नहीं जानता। लेकिन, हाथ से कोडिंग करना बहुत मुश्किल नहीं है। आप जिस सरणी के साथ काम कर रहे हैं, उसके तत्वों को मानते हुए NSNumber पूर्णांक प्रकार की वस्तुएं हैं, और 'गिरफ्तार' NSMutableArray है जिसे आप रिवर्स करना चाहते हैं।

int n = [arr count];
for (int i=0; i<n/2; ++i) {
  id c  = [[arr objectAtIndex:i] retain];
  [arr replaceObjectAtIndex:i withObject:[arr objectAtIndex:n-i-1]];
  [arr replaceObjectAtIndex:n-i-1 withObject:c];
}

जब से आप एक NSArray के साथ शुरू करते हैं तब आपको मूल NSArray ('OrigArray') की सामग्री के साथ सबसे पहले उत्परिवर्तित सरणी बनाना होगा।

NSMutableArray * arr = [[NSMutableArray alloc] init];
[arr setArray:origArray];

संपादित करें: ब्रेंट के उत्तर में सुझावों के कारण लूप काउंट में फिक्स्ड n -> n / 2 और NSNumber को अधिक सामान्य आईडी में बदल दिया।


1
क्या यह ग पर जारी नहीं है?
क्ले ब्रिजल्स

0

यदि आप सब करना चाहते हैं तो रिवर्स में पुनरावृति है, यह कोशिश करें:

// iterate backwards
nextIndex = (currentIndex == 0) ? [myArray count] - 1 : (currentIndex - 1) % [myArray count];

आप [myArrayCount] एक बार कर सकते हैं और इसे एक स्थानीय चर में सहेज सकते हैं (मुझे लगता है कि यह महंगा है), लेकिन मैं यह भी अनुमान लगा रहा हूं कि कंपाइलर कोड के साथ वही काम करेगा जो ऊपर लिखा गया है।



0

इसे इस्तेमाल करे:

for (int i = 0; i < [arr count]; i++)
{
    NSString *str1 = [arr objectAtIndex:[arr count]-1];
    [arr insertObject:str1 atIndex:i];
    [arr removeObjectAtIndex:[arr count]-1];
}

0

यहाँ एक अच्छा मैक्रो है जो NSMutableArray या NSArray के लिए काम करेगा :

#define reverseArray(__theArray) {\
    if ([__theArray isKindOfClass:[NSMutableArray class]]) {\
        if ([(NSMutableArray *)__theArray count] > 1) {\
            NSUInteger i = 0;\
            NSUInteger j = [(NSMutableArray *)__theArray count]-1;\
            while (i < j) {\
                [(NSMutableArray *)__theArray exchangeObjectAtIndex:i\
                                                withObjectAtIndex:j];\
                i++;\
                j--;\
            }\
        }\
    } else if ([__theArray isKindOfClass:[NSArray class]]) {\
        __theArray = [[NSArray alloc] initWithArray:[[(NSArray *)__theArray reverseObjectEnumerator] allObjects]];\
    }\
}

बस कॉल का उपयोग करने के लिए: reverseArray(myArray);

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.