Ruby 中如何进行校验位计算?

发布于 2024-12-01 16:19:56 字数 472 浏览 1 评论 0原文

我正在尝试在 Ruby 中为 FedEx 跟踪号码构建校验位计算。

以下是校验位计算的信息和步骤:

  • 数字位置从右到左标记。
  • 数字 1 是校验字符。
  • 不使用数字 16 到 22。

步骤:

  1. 从位置 2 开始,将偶数编号位置的值相加。
  2. 将步骤一的结果乘以三。
  3. 从位置 3 开始,将奇数位置的值相加。请记住 – 位置 1 是您要计算的校验位。
  4. 将第二步的结果与第三步的结果相加。
  5. 确定最小的数字,将其与步骤 4 中的数字相加后得到 10 的倍数。这是校验位。

以下是该流程的示例(由 FedEx 提供): 在此处输入图像描述

那么,如何在 Ruby 中实现此功能?

I'm trying to build a check-digit calculation in Ruby for FedEx tracking numbers.

Here is info and the steps for the check-digit calculation:

  • Digit positions are labeled from right to left.
  • Digit 1 is the check character.
  • Digits 16 through 22 are not used.

Steps:

  1. Starting from position 2, add up the values of the even numbered positions.
  2. Multiply the results of step one by three.
  3. Starting from position 3, add up the values of the odd numbered positions. Remember – position 1 is the check digit you are trying to calculate.
  4. Add the result of step two to the result of step three.
  5. Determine the smallest number which when added to the number from step four results in a multiple of 10. This is the check digit.

Here is an example of the process (provided by FedEx):
enter image description here

So, how do I implement this in Ruby?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

巴黎盛开的樱花 2024-12-08 16:19:56

当您将数字作为字符串时(或者如果您将数字作为整数,只需在其上使用 #to_s 并获取字符串),然后您可以简单地从其中提取数字:

number_string[idx].to_i

或者如果您使用 Ruby 1.8

number_string[idx..idx].to_i

#to_i 就是将其转为整数,这样就可以添加到其他的了。然后只需继续执行提供的步骤即可计算您的数字。

要实现它,您所要做的就是将指令中提供的位置正确映射到数字字符串表示形式中的 idx 索引位置。只需在纸上进行计算,在 head 中进行计数,或者在 Ruby 中使用负 idx(从字符串末尾开始计数)。

编辑

解决方案可能是这样的:

bar_code_data = "961102098765431234567C"
digits_with_position = bar_code_data.reverse[1..14].split(//).map(&:to_i).zip(2..1/0.0)

如下所示:

  • reverse - 反转字符串,所以现在我们可以从左到右计数而不是反向
  • [ 1..14] - 选择我们感兴趣的字符子串(Ruby 从 0 开始计数)
  • split(//) - 将一个字符串拆分为长度为 1 个字符的子字符串,换句话说 - 单独的数字
  • map(&:to_i) - 对数组的每个元素调用 #to_i,换句话说,转换为整数
  • zip(2..1/0.0) - 添加起始位置从 2 到无穷大,到每个元素

现在我们应该有这样的东西:

[[7, 2],
[6, 3],
[5, 4],
[4, 5],
[3, 6],
[2, 7],
[1, 8],
[3, 9],
[4, 10],
[5, 11],
[6, 12],
[7, 13],
[8, 14],
[9, 15]]

sum = digits_with_position.map{|i| i[0] * (i[1].even? ? 3 : 1)}.reduce(+:)

我们在算法上做了很少的改变,这对你来说应该不难理解:

而不是:

sum = (in[2] + in[4] + in[6] + ...)*3 + (in[3] + in[5] + in[7] + ...)

我们做了:

sum = in[2]*3 + in[3]*1 + in[4]*3 + in[5]*1 + in[6]*3 + in[7]*1 + ...

这是相同的结果,但改变了操作顺序。

另外:

  • map {|i| ... } - 映射列表的每个值,在我们的例子中 i 是元组,一对 [digit,pos]
  • i[1].even? - 检查位置是否为偶数
  • <代码> i [1] .even? ? 3 : 1 - 对于偶数位置使用 3,对于相反(奇数)位置仅使用 1
  • reduce(:+) - 使用 + 操作将结果数组减少为单个值(添加所有结果)

现在有趣的部分:-)

check_code = 10 - (sum % 10)
  • sum % 10 - 总和值的模块 10,返回 sum 除以 10 的提醒,在我们的例子中是最后一个数字
  • 10 - (sum % 10) -对最接近的不小于 10 倍数的补码

描述中存在错误,因为如果结果是 130,那么下一个更大的 10 倍数是 140,差值是 10,这不是数字的正确结果(它可能应该是 0) 。

其他更快的解决方案是这样的(展开所有循环,只需对所有内容进行硬编码):

d = "961102098765431234567C".split(//) # avoid having to use [-2..-2] in Ruby 1.8
sum_even = d[-2].to_i + d[-4].to_i + d[-6].to_i + d[-8].to_i + d[-10].to_i + d[-12].to_i + d[-14].to_i
sum_odd = d[-3].to_i + d[-5].to_i + d[-7].to_i + d[-9].to_i + d[-11].to_i + d[-13].to_i + d[-15].to_i
sum = sum_even * 3 + sum_odd
check_code = 10 - sum % 10

这只是非常简单的解决方案,不值得解释,除非有人要求它

When you have your number as string (or if you have your digit as integer, just #to_s on it and get string), and then you can simply extract digits from there with:

number_string[idx].to_i

or if you use Ruby 1.8

number_string[idx..idx].to_i

#to_i is to convert it to integer, so you can add it to others. Then just proceed with steps provided to calculate your number.

All you have to do to implement it is correctly map positions provided in instruction to idx index position in your string representation of number. Just do it on paper with counting in head or use negative idx (it counts from end of the string) in Ruby.

EDIT:

The solution could be something like this:

bar_code_data = "961102098765431234567C"
digits_with_position = bar_code_data.reverse[1..14].split(//).map(&:to_i).zip(2..1/0.0)

this goes as follow:

  • reverse - reverse string, so now we can count from left to right instead of reverse
  • [1..14] - select substrig of characters, which we're interested in (Ruby counts from 0)
  • split(//) - split one string into substrings of length 1 character, in other words - separate digits
  • map(&:to_i) - call #to_i on every element of array, in other words convert to integer
  • zip(2..1/0.0) - add position starting from 2 to Infinity, to every element

Now we should have something like this:

[[7, 2],
[6, 3],
[5, 4],
[4, 5],
[3, 6],
[2, 7],
[1, 8],
[3, 9],
[4, 10],
[5, 11],
[6, 12],
[7, 13],
[8, 14],
[9, 15]]

sum = digits_with_position.map{|i| i[0] * (i[1].even? ? 3 : 1)}.reduce(+:)

We made little change in algorithm, which should not be hard to you to follow:

instead of:

sum = (in[2] + in[4] + in[6] + ...)*3 + (in[3] + in[5] + in[7] + ...)

we made:

sum = in[2]*3 + in[3]*1 + in[4]*3 + in[5]*1 + in[6]*3 + in[7]*1 + ...

which is the same result, but with changed order of operations.

Also:

  • map {|i| ... } - map every value of list, i is tuple in our case, pair of [digit,pos]
  • i[1].even? - check if position is even
  • i[1].even? ? 3 : 1 - for even position use 3, for opposite (odd) use just 1
  • reduce(:+) - reduce resulting array to single value using + operation (add all results)

Now fun part :-)

check_code = 10 - (sum % 10)
  • sum % 10 - module 10 of sum value, return reminder of division sum by 10, which in our case is last digit
  • 10 - (sum % 10) - complement to nearest not smaller multiple of 10

There is error in description, because if you would have 130 as result, then next bigger multiple of 10 is 140 and difference is 10, which is not correct result for digit (it should probably be 0).

Other faster solution would be like this (unroll all loops, just hardcode everything):

d = "961102098765431234567C".split(//) # avoid having to use [-2..-2] in Ruby 1.8
sum_even = d[-2].to_i + d[-4].to_i + d[-6].to_i + d[-8].to_i + d[-10].to_i + d[-12].to_i + d[-14].to_i
sum_odd = d[-3].to_i + d[-5].to_i + d[-7].to_i + d[-9].to_i + d[-11].to_i + d[-13].to_i + d[-15].to_i
sum = sum_even * 3 + sum_odd
check_code = 10 - sum % 10

It's just dead simple solution, not worth explaining, unless someone asks for it

没有你我更好 2024-12-08 16:19:56

将您的号码传递给以下方法,它将返回附加有校验和数字的号码。
使用的参考来自:https://www.gs1.org/services/如何手动计算校验位

def add_check_digit(code_value) 
   sum = 0
   code_value.to_s.split(//).each_with_index{|i,index| sum = sum + (i[0].to_i * ((index+1).even? ? 3 : 1))}
   check_digit = sum.zero? ? 0 : (10-(sum % 10))
   return (code_value.to_s.split(//)<<check_digit).join("")
end

Pass your number to below method and it will return the number appended with a checksum digit.
Reference used from: https://www.gs1.org/services/how-calculate-check-digit-manually

def add_check_digit(code_value) 
   sum = 0
   code_value.to_s.split(//).each_with_index{|i,index| sum = sum + (i[0].to_i * ((index+1).even? ? 3 : 1))}
   check_digit = sum.zero? ? 0 : (10-(sum % 10))
   return (code_value.to_s.split(//)<<check_digit).join("")
end
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文