卡片按数量排序和调整等级的功能

Function for ranking cards by number and adjusting ranks

这是一个不特定于语言的开放式问题,尽管我最终要编写的代码将是 Ruby/Rails。

我的应用程序中有一个页面,其中包含一组卡片,每张卡片都有一些内容。每张卡片都有一个正整数等级属性。我正在研究一种方法,让用户可以通过单击卡片上的 up/down 按钮来重新排序卡片,这需要调整排名。例如,如果我想将#2 向上移动,我需要与#1 交换位置(如果有#1)。如果我想将 #1 向下移动,我需要与该组中的最后一张牌交换位置。

我的问题是我有很多坏数据要处理。我的很多卡片都没有等级 (rank = nil)。此外,个人资料中的某些卡片没有按顺序排列。排名可能是 [1,3,7,99,nil] 而不是 [1,2,3,4,5]。排名为 nil 的卡片出现在列表末尾。

如果数据完美,编写一个函数来调整卡片的等级会相当容易,但这里不是这样。我想知道是否有关于如何解决此问题的任何好的资源。我想确保我考虑了所有可能的情况,例如当我将一张 rank = 4 向上移动一张 space,但前一个插槽中的卡也有 [=15] 时该怎么办=].

作为一个额外的挑战,这个函数将在控制器中出现,所以我正在寻找一种方法来降低复杂性,这样我就不会在控制器方法中有 100 多行代码。

用有效代码回答

如果对其他人有帮助,这里是我为具有不同 类:

的排名卡想出的工作代码
def adjust_rank
    company = current_user.company

    # Build array of cards
    cards = []
    cards << company.office_card if company.office_card.present?
    cards << company.twitter_card if company.twitter_card.present?
    company.gallery_cards.each { |gc| cards << gc }
    company.quote_cards.each { |qc| cards << qc }
    company.wild_cards.each { |wc| cards << wc }

    # Ensure no cards have nil for rank before sorting
    cards.each_with_index { |c,i| c.rank = 100+i if c.rank.nil? }
    cards.sort_by! { |x| x.rank }

    # Ensure cards are ranked sequentially
    cards.each_with_index { |c, i| c.rank = i }

    # Save all cards in a single transaction. This works even though not all the cards belong to the same class.
    cards[0].class.transaction do
        cards.each { |c| c.save }
    end

    # This is the card whose rank the user is adjusting
    selected = Object.const_get(rank_params[:type]).find(rank_params[:id])

    # Get the adjacent card from the cards array
    if rank_params[:direction] == 'up'
        swap = cards.select { |c| c.rank == selected.rank + 1 }[0]
        # If there is no card after selected, get first card in array
        swap = cards[0] if swap.nil?
    elsif rank_params[:direction] == 'down'
        swap = cards.select { |c| c.rank == selected.rank - 1 }[0]
        # If there is no card before selected, get last card in array
        swap = cards[-1] if swap.nil?
    end

    # Swap ranks
    swap_new_rank = selected.rank
    selected_new_rank = swap.rank

    swap.rank = swap_new_rank
    selected.rank = selected_new_rank

    respond_to do |format|
        if swap.save && selected.save
            format.json { render json: {}, status: :ok }
        else
            format.json { render json: {}, status: :bad_request }
        end
    end
end

我对内容排名做了类似的事情。我在 Rails 中构建了一个 CMS,让用户可以对内容显示的位置进行排序。

为了提供一个与平台无关的示例,我将使用一些伪代码。我还假设您将使用某种 ORM,例如 ActiveRecord。

Rails 事情的一面


无论如何,我的解决方案是采用混合 Javascript/Backend 方法。我首先创建我的 Card 模型。迁移可能看起来像这样 -

create_table :cards do |t|
    t.string :name
    t.integer :position, :default => 0
end

注意我有一个位置。这对于 id 来说似乎是多余的,但我将使用它来存储我们对象的视觉位置。另请注意,我为我们的 position 提供了一个默认值,因此我们永远不会有像上面那样的错误数据。

那么,假设我们有以下种子数据 -

Card.create(name: 'card1', position: 1) 
Card.create(name: 'card2', position: nil) 
Card.create(name: 'card3', position: 6) 
Card.create(name: 'card4', position: 5) 

请注意,尽管我们的卡片可能按顺序标记,但它们的位置可以自由更改。如果我们要获取 Card.all 并根据 position 对它们进行排序,我们将得到一个看起来像这样的列表,假设我们正在输出他们的名字。

card2 # position 0 since our default is 0
card1 # position 1
card4 # position 5
card3 # position 6

Javascript 侧面


现在,我们希望能够重新订购我们的卡片。由于创建前端有很多解决方案,我将更抽象地解释这部分。

在这个例子中,我假设每张卡片都在它自己的 div 元素中,并且在 div 元素中是一个隐藏的形式,它决定了 position

基本上,当您单击箭头以在卡片堆中向上或向下移动项目时,您需要重新评估它们的位置。代码可能看起来像这样:

function whenArrowClicked(){
    for every card div{
        this.form.val(i)
    }
}

所以,我们的初步定位

card2 # position 0
card1 # position 1
card4 # position 5
card3 # position 6

会更像

card2 # position 1
card1 # position 2
card4 # position 3
card3 # position 4

这更有意义!

此外,如果我们要将 card1 上移一个位置,我们的 javascript 会为我们固定位置,因此 position 值反映视觉值。

+ card1 # position 1 - used to be 2
- card2 # position 2 - used to be 1
  card4 # position 3
  card3 # position 4

保存此表单将更新我们的对象并修复所有具有 nil 位置开头的对象。


请记住,这是一个非常简短的解决方案,实际实施需要考虑更多细节 - 例如,如果您计划拥有大量卡片,Card.all 将不是一个获取内容的有效方式。此外,您可能 运行 遇到尝试一次保存多个模型的问题。 This might help(见评论)。但是,希望这可以帮助您了解如何构建您的应用程序。