Метод Swift sorted(by:) неправильно сортирует массив структур на основе целого числа

Мне нужно отсортировать массив некоторых пользовательских структурных объектов. Моя структура содержит значение Int "user_id", которое я должен использовать для сортировки массива. Пока ничего особенно интересного.

И тогда я должен заставить один элемент (на основе его user_id) оставаться в верхней части моего списка, т.е. как первый элемент моего отсортированного массива.

Я пришел с текущим кодом:

myArray.sorted(by: { $0.user_id == 40 ? true : $0.user_id < $1.user_id })

но иногда (не всегда) кажется, что массив полностью игнорирует первое сравнение и просто сортирует элементы на основе их user_id. Так, например, если в моем списке есть элементы 37, 40, 41, он будет отсортирован как [40, 37, 41] или [37, 40, 41], и я не могу найти, почему и как.

Для контекста: я использую этот список в динамической переменной (я забыл, как они называются) в представлении SwiftUI, которое использует ForEach(mySortedArray) для создания VStack из отсортированных пользовательских строк. Таким образом, в основном код SwiftUI выглядит так (BoardMember — моя пользовательская структура):

    @State private var boardPerms: [BoardMember] = []

    func fetchMembers() {
        boardPerms = ...
    }

    var displayedMembers: [BoardMember] {
        boardPerms.sorted(by: { $0.user_id == 40 ? true : $0.user_id < $1.user_id })
    }

    var body: some View {
    VStack {
        ForEach(displayedMembers) { member in
            ...
        }
        .onAppear(perform: fetchMembers)
    }

и я могу заверить, что проблема в переменной displayedMembers, потому что я также пытался отладить ее с помощью следующего кода внутри своего тела: Text((displayedMembers.map{$0.user_id.description}).joined(separator: " "))

Есть идеи, что не так с моим сравнением? Спасибо за помощь!

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
61
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Предикат Array.sorted(по:) требует, чтобы вы

return true if its first argument should be ordered before its second argument; otherwise, false.

Порядок аргументов, передаваемых предикату, может быть указан в любом порядке ((a, b)или(b, a)) в зависимости от того, как список был отсортирован до сих пор, но предикат должен дает согласованный результат в любом порядке, иначе вы получите бессмысленные результаты. .

В вашем предикате вы проверяете идентификатор пользователя элемента первый, чтобы определить его приоритет, но нет — второй; т. е. вы обрабатываете (a, b), но нет(b, a). Если вы обновите свой предикат до

myArray.sorted(by: {
    // The order of the checking here matters. Specifically, the predicate
    // requires a `<` relationship, not a `<=` relationship. If both $0 and $1
    // have the same `user_id`, we need to return `false`. Checking $1.user_id
    // first hits two birds with one stone.
    if $1.user_id == 40 {
        return false
    } else if $0.user_id == 40 {
        return true
    }
    
    return $0.user_id < $1.user_id
})

тогда вы получите стабильные результаты.

В качестве альтернативы, если элемент известен заранее, вы можете избежать этого, извлекая его из списка и сортируя оставшиеся результаты.

О, я совершенно не подумал о сортировке (б, а), большое спасибо! И да, я подумаю об отделении 1-го элемента от остального списка, хорошая идея.

Z_runner 16.03.2022 15:13

Разве предикат не должен возвращать false для двух элементов равный, например. если оба идентификатора пользователя равны 40?

Martin R 16.03.2022 15:14

@MartinR Это правда, хотя звучит так, будто user_id — уникальное значение. Это также зависит от того, имеет ли значение стабильный порядок, поскольку, если у вас есть n копии с одинаковым значением, сортировка будет поднимать их все до начала списка, просто в нестабильном порядке. Однако я обновлю ответ, чтобы включить это.

Itai Ferber 16.03.2022 15:15

Будет проще, если сначала протестировать if $1.user_id == 40 :)

Martin R 16.03.2022 15:21

Предикат должен быть «строгим слабым порядком элементов» и, в частности, иррефлексивным. Я понятия не имею, что может случиться, если это не будет удовлетворено.

Martin R 16.03.2022 15:22

@MartinR О, да. Спасибо, обновил.

Itai Ferber 16.03.2022 15:34

@LeoDabus Спасибо, исправил опечатку. Вот что я получаю за кодирование в браузере ??‍♂️

Itai Ferber 16.03.2022 17:25

Другие вопросы по теме