Каков наилучший способ перетащить NSMutableArray?

Если у вас есть NSMutableArray , как вы произвольно NSMutableArray элементы?

(У меня есть свой собственный ответ для этого, который размещен ниже, но я новичок в Cocoa, и мне интересно узнать, есть ли лучший способ.)


Обновление. Как отмечено @Mukesh, с iOS 10+ и macOS 10.12+ существует метод -[NSMutableArray shuffledArray] который можно использовать для перетасовки. Подробнее см. Https://developer.apple.com/documentation/foundation/nsarray/1640855-shuffledarray?language=objc . (Но обратите внимание, что это создает новый массив, а не перетасовку элементов на месте.)

Вам не нужен метод swapObjectAtIndex. exchangeObjectAtIndex: withObjectAtIndex: уже существует.

Я решил это, добавив категорию в NSMutableArray.

Редактировать: Удаленный ненужный метод благодаря ответу Лэдда.

Изменить: Изменено (arc4random() % nElements) до arc4random_uniform(nElements) благодаря ответу Григория Гольцова и комментариям miho и blahdiblah

Редактировать: улучшение петли, благодаря комментарию Рона

Изменить: добавлена ​​проверка того, что массив не пуст, благодаря комментарию Махеша Агравала

 // NSMutableArray_Shuffling.h #if TARGET_OS_IPHONE #import  #else #include  #endif // This category enhances NSMutableArray by providing // methods to randomly shuffle the elements. @interface NSMutableArray (Shuffling) - (void)shuffle; @end // NSMutableArray_Shuffling.m #import "NSMutableArray_Shuffling.h" @implementation NSMutableArray (Shuffling) - (void)shuffle { NSUInteger count = [self count]; if (count <= 1) return; for (NSUInteger i = 0; i < count - 1; ++i) { NSInteger remainingCount = count - i; NSInteger exchangeIndex = i + arc4random_uniform((u_int32_t )remainingCount); [self exchangeObjectAtIndex:i withObjectAtIndex:exchangeIndex]; } } @end 

Поскольку я еще не могу прокомментировать, я думал, что внес вам полный ответ. Я изменил реализацию Кристофера Джонсона для своего проекта несколькими способами (на самом деле пытаюсь сделать его максимально сжатым), один из которых – arc4random_uniform() поскольку он избегает arc4random_uniform() смещения .

 // NSMutableArray+Shuffling.h #import  /** This category enhances NSMutableArray by providing methods to randomly * shuffle the elements using the Fisher-Yates algorithm. */ @interface NSMutableArray (Shuffling) - (void)shuffle; @end // NSMutableArray+Shuffling.m #import "NSMutableArray+Shuffling.h" @implementation NSMutableArray (Shuffling) - (void)shuffle { NSUInteger count = [self count]; for (uint i = 0; i < count - 1; ++i) { // Select a random element between i and end of array to swap with. int nElements = count - i; int n = arc4random_uniform(nElements) + i; [self exchangeObjectAtIndex:i withObjectAtIndex:n]; } } @end 

Из iOS 10 вы можете использовать новый shuffled API:

https://developer.apple.com/reference/foundation/nsarray/1640855-shuffled

 let shuffledArray = array.shuffled() 

Немного улучшенное и сжатое решение (по сравнению с верхними ответами).

Алгоритм тот же и описан в литературе как « Fisher-Yates shuffle ».

В ObjectiveC:

 @implementation NSMutableArray (Shuffle) // Fisher-Yates shuffle - (void)shuffle { for (NSUInteger i = self.count; i > 1; i--) [self exchangeObjectAtIndex:i - 1 withObjectAtIndex:arc4random_uniform((u_int32_t)i)]; } @end 

В Swift 3.2 и 4.x:

 extension Array { /// Fisher-Yates shuffle mutating func shuffle() { for i in stride(from: count - 1, to: 0, by: -1) { swapAt(i, Int(arc4random_uniform(UInt32(i + 1)))) } } } 

В Swift 3.0 и 3.1:

 extension Array { /// Fisher-Yates shuffle mutating func shuffle() { for i in stride(from: count - 1, to: 0, by: -1) { let j = Int(arc4random_uniform(UInt32(i + 1))) (self[i], self[j]) = (self[j], self[i]) } } } 

Примечание. Более сжатое решение в Swift возможно из iOS10 с использованием GameplayKit .

Примечание. Также доступен алгоритм для нестабильной перетасовки (со всеми позициями, которые необходимо изменить, если count> 1)

Это самый простой и быстрый способ перетасовать NSArrays или NSMutableArrays (головоломки объектов – это NSMutableArray, он содержит объекты головоломки. Я добавил к индексу переменной объекта головоломки, который указывает начальную позицию в массиве)

 int randomSort(id obj1, id obj2, void *context ) { // returns random number -1 0 1 return (random()%3 - 1); } - (void)shuffle { // call custom sort function [puzzles sortUsingFunction:randomSort context:nil]; // show in log how is our array sorted int i = 0; for (Puzzle * puzzle in puzzles) { NSLog(@" #%d has index %d", i, puzzle.index); i++; } } 

выход журнала:

  #0 has index #6 #1 has index #3 #2 has index #9 #3 has index #15 #4 has index #8 #5 has index #0 #6 has index #1 #7 has index #4 #8 has index #7 #9 has index #12 #10 has index #14 #11 has index #16 #12 has index #17 #13 has index #10 #14 has index #11 #15 has index #13 #16 has index #5 #17 has index #2 

вы можете также сравнить obj1 с obj2 и решить, какие вы хотите вернуть возможные значения:

  • NSOrderedAscending = -1
  • NSOrderedSame = 0
  • NSOrderedDescending = 1

Существует хорошая популярная библиотека, которая имеет этот метод, поскольку это часть, называемая SSToolKit в GitHub . Файл NSMutableArray + SSToolkitAdditions.h содержит метод тасования. Вы также можете использовать его. Среди этого, похоже, много полезных вещей.

Главная страница этой библиотеки находится здесь .

Если вы используете это, ваш код будет выглядеть так:

 #import  NSMutableArray *tableData = [NSMutableArray arrayWithArray:[temp shuffledArray]]; 

В этой библиотеке также есть Pod (см. CocoaPods)

Из iOS 10 вы можете использовать NSArray shuffled() из GameplayKit . Вот помощник для Array в Swift 3:

 import GameplayKit extension Array { @available(iOS 10.0, macOS 10.12, tvOS 10.0, *) func shuffled() -> [Element] { return (self as NSArray).shuffled() as! [Element] } @available(iOS 10.0, macOS 10.12, tvOS 10.0, *) mutating func shuffle() { replaceSubrange(0.. 

Если элементы повторяются.

например, массив: AAABB или BBAAA

только решение: ABABA

sequenceSelected – это NSMutableArray, в котором хранятся элементы classа obj, которые являются указателями на некоторую последовательность.

 - (void)shuffleSequenceSelected { [sequenceSelected shuffle]; [self shuffleSequenceSelectedLoop]; } - (void)shuffleSequenceSelectedLoop { NSUInteger count = sequenceSelected.count; for (NSUInteger i = 1; i < count-1; i++) { // Select a random element between i and end of array to swap with. NSInteger nElements = count - i; NSInteger n; if (i < count-2) { // i is between second and second last element obj *A = [sequenceSelected objectAtIndex:i-1]; obj *B = [sequenceSelected objectAtIndex:i]; if (A == B) { // shuffle if current & previous same do { n = arc4random_uniform(nElements) + i; B = [sequenceSelected objectAtIndex:n]; } while (A == B); [sequenceSelected exchangeObjectAtIndex:i withObjectAtIndex:n]; } } else if (i == count-2) { // second last value to be shuffled with last value obj *A = [sequenceSelected objectAtIndex:i-1];// previous value obj *B = [sequenceSelected objectAtIndex:i]; // second last value obj *C = [sequenceSelected lastObject]; // last value if (A == B && B == C) { //reshufle sequenceSelected = [[[sequenceSelected reverseObjectEnumerator] allObjects] mutableCopy]; [self shuffleSequenceSelectedLoop]; return; } if (A == B) { if (B != C) { [sequenceSelected exchangeObjectAtIndex:i withObjectAtIndex:count-1]; } else { // reshuffle sequenceSelected = [[[sequenceSelected reverseObjectEnumerator] allObjects] mutableCopy]; [self shuffleSequenceSelectedLoop]; return; } } } } } 
 NSUInteger randomIndex = arc4random() % [theArray count]; 

Ответ Кристофера Джонсона довольно приятный, но он не совсем случайный.

Учитывая массив из 2-х элементов, эта функция всегда возвращает инвертированный массив, потому что вы генерируете диапазон вашего случайного значения по остальным индексам. Более точная функция shuffle() будет

 - (void)shuffle { NSUInteger count = [self count]; for (NSUInteger i = 0; i < count; ++i) { NSInteger exchangeIndex = arc4random_uniform(count); if (i != exchangeIndex) { [self exchangeObjectAtIndex:i withObjectAtIndex:exchangeIndex]; } } } 

Изменить: это неверно. Для справки я не удалял этот пост. См. Комментарии по причине неправильного подхода.

Простой код здесь:

 - (NSArray *)shuffledArray:(NSArray *)array { return [array sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) { if (arc4random() % 2) { return NSOrderedAscending; } else { return NSOrderedDescending; } }]; } 
Interesting Posts

неопределенная ссылка на `WinMain @ 16 ‘

Как назначить имя столбцу size ()?

Как мне несколько вставить несколько записей?

Настройка времени ожидания для веб-службы .NET

Android: не удалось преобразовать @ drawable / picture в drawable

Кордова InAppBrowser – Как отключить URL и панель навигации?

Как использовать Simple Ajax Beginform в Asp.net MVC 4?

Пользовательский одиночный выбор ListView

Как обнаружить простые геометрические фигуры с помощью OpenCV

В чем разница между свойством зависимостей и прикрепленным свойством в WPF?

ОШИБКА: Имя источника данных не найдено и указанный драйвер по умолчанию не указан

При использовании макросов VM с VMWare, как использовать ключ командной строки в VM?

Почему Chrome и IE помещают «Mozilla 5.0» в User-Agent, который они отправляют на сервер?

Laravel перенаправляет обратно в исходное место после входа в систему

Каков правильный код ответа REST для действительного запроса, но пустые данные?

Давайте будем гением компьютера.