卡片按数量排序和调整等级的功能
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(见评论)。但是,希望这可以帮助您了解如何构建您的应用程序。
这是一个不特定于语言的开放式问题,尽管我最终要编写的代码将是 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(见评论)。但是,希望这可以帮助您了解如何构建您的应用程序。