Skip to content

Instantly share code, notes, and snippets.

@ebraminio
Last active July 2, 2024 06:14
Show Gist options
  • Save ebraminio/5292017 to your computer and use it in GitHub Desktop.
Save ebraminio/5292017 to your computer and use it in GitHub Desktop.
Check Iranian National Code Validity - بررسی صحت کد ملی ایرانی - Clojure, C#, F#, Ruby, JavaScript, Dart, Python, Scala, Java 8, PHP, C, Go, Swift, Kotlin, Groovy, Rust, Haskell, Erlang, Elixir, PowerQuery M Language, VBA, R, Lua, Fortran, Pascal/Delphi, Excel, Stored Procedure / MySQL
// Check Iranian National Code Validity - Clojure, C#, F#, Ruby, JavaScript, Dart, Python, Scala, Java 8, PHP, C, Go, Swift, Kotlin, Groovy, Rust, Haskell, Erlang, Elixir, Power Query M Language, VBA, R, Lua, Fortran, Pascal/Delphi, Excel, Stored Procedure / MySQL
// بررسی صحت کد ملی ایران - کلوژر، سی‌شارپ، اف‌شارپ، روبی، جاوااسکریپت، دارت، پایتون، اسکالا، جاوا ۸، پی‌اچ‌پی، سی، گو، سوئیفت، کاتلین، گرووی، راست، هسکل، ارلنگ، الکسیر، پاورکوئری ام، وی‌بی ای، آر، لوآ، فرترن، پاسکال/دلفی، اکسل، مای‌اس‌کیوال
// در نسخه‌های قبل یکسان بودن اعداد نا معتبر تشخیص داده می‌شد ولی
// اعداد یکسان نامعتبر نیست https://web.archive.org/web/20170706081048/http://www.fardanews.com/fa/news/127747/رندترین-شماره-ملی-بلای-جان-صاحبش-شد
// بعضی از پیاده‌سازی‌ها سریع نیستند، می‌توانید نسخهٔ خود را بر پایهٔ
// نسخهٔ سی یا گو ایجاد کنید که بهترین سرعت را داشته باشد
/**
* @author Ebrahim Byagowi (2013-)
* @license: Public Domain
*/
// Clojure
(defn valid-iran-code? [input]
(if (re-matches #"^\d{10}$" input)
(let
[check (Integer/parseInt (subs input 9 10))
sum (mod (reduce + (map (fn [x] (* (Integer/parseInt
(subs
input x (+ x 1)))
(- 10 x)))
(range 9))) 11)]
(if (< sum 2) (== check sum) (== (+ check sum) 11))
false))
// Ruby
def is_valid_iran_code(input)
return false if /^\d{10}$/ !~ input
check = Integer(input[9])
s = (0..8).sum {|x| Integer(input[x]) * (10 - x)} % 11
s < 2 ? check == s : check + s == 11
end
// C#
using System.Text.RegularExpressions;
public static bool IsValidIranianNationalCode(string input)
{
if (!Regex.IsMatch(input, @"^\d{10}$"))
return false;
var check = Convert.ToInt32(input.Substring(9, 1));
var sum = Enumerable.Range(0, 9)
.Select(x => Convert.ToInt32(input.Substring(x, 1)) * (10 - x))
.Sum() % 11;
return sum < 2 ? check == sum : check + sum == 11;
}
// F#
open System.Text.RegularExpressions
let isValidIranianNationalCode input =
if Regex.IsMatch(input, @"^\d{10}$") then
let check = Convert.ToInt32(input.Substring(9, 1))
let sum = (seq { 0 .. 8 }
|> Seq.map (fun x -> Convert.ToInt32(input.Substring(x, 1)) * (10 - x))
|> Seq.sum) % 11
if sum < 2 then check = sum else check + sum = 11
else
false
// JavaScript
function isValidIranianNationalCode(input) {
if (!/^\d{10}$/.test(input)) return false;
const check = +input[9];
const sum = input.split('').slice(0, 9).reduce((acc, x, i) => acc + +x * (10 - i), 0) % 11;
return sum < 2 ? check === sum : check + sum === 11;
}
// Dart
bool isValidIranianNationalCode(String input) {
if (!RegExp("^\\d{10}").hasMatch(input)) return false;
final check = int.parse(input[9]);
final sum = Iterable<int>.generate(9).map((x) => int.parse(input[x]) * (10 - x)).reduce((x, y) => x + y) % 11;
return sum < 2 ? check == sum : check + sum == 11;
}
// C
bool isValidIranianNationalCode(const char *input) {
for (unsigned i = 0; i < 10; ++i) if (input[i] < '0' || input[i] > '9') return false;
if (input[10]) return false;
unsigned check = input[9] - '0';
unsigned sum = 0;
for (unsigned i = 0; i < 9; ++i) sum += (int)(input[i] - '0') * (10 - i);
sum %= 11;
return sum < 2 ? check == sum : check + sum == 11;
}
// Go
func isValidIranianNationalCode(input string) bool {
for i := 0; i < 10; i++ {
if input[i] < '0' || input[i] > '9' {
return false
}
}
check := int(input[9] - '0')
sum := 0
for i := 0; i < 9; i++ {
sum += int(input[i]-'0') * (10 - i)
}
sum %= 11
return (sum < 2 && check == sum) || (sum >= 2 && check+sum == 11)
}
// Python
import re
def is_valid_iran_code(input: str) -> bool:
if not re.search(r'^\d{10}$', input): return False
check = int(input[9])
s = sum(int(input[x]) * (10 - x) for x in range(9)) % 11
return check == s if s < 2 else check + s == 11
// Scala
def isValidIranianNationalCode(input: String) = {
val pattern = """^(\d{10})$""".r
input match {
case pattern(_) =>
val check = input.substring(9, 10).toInt
val sum = (0 to 8)
.map(x => input.substring(x, x + 1).toInt * (10 - x))
.sum % 11
if (sum < 2) check == sum else check + sum == 11
case _ => false
}
}
// Java 8
import java.util.stream.Streams;
import java.util.function.IntUnaryOperator;
public static boolean isValidIranianNationalCode(String input) {
if (!input.matches("^\\d{10}$"))
return false;
int check = Integer.parseInt(input.substring(9, 10));
int sum = IntStream.range(0, 9)
.map(x -> Integer.parseInt(input.substring(x, x + 1)) * (10 - x))
.sum() % 11;
return sum < 2 ? check == sum : check + sum == 11;
}
// PHP
function isValidIranianNationalCode(string $input): boolean {
if (!preg_match("/^\d{10}$/", $input)) {
return false;
}
$check = (int) $input[9];
$sum = array_sum(array_map(function ($x) use ($input) {
return ((int) $input[$x]) * (10 - $x);
}, range(0, 8))) % 11;
return $sum < 2 ? $check == $sum : $check + $sum == 11;
}
// Swift, based on @farzadshbfn work but modified a little, tested with Swift 5.3.1
func isValidIranianNationalCode(input: String) -> Bool {
let digits = input.compactMap { Int(String($0)) }
guard digits.count == 10 && digits.count == input.count else {
return false
}
let check = digits[9]
let sum = digits[0 ..< 9].enumerated().reduce(0) { $0 + $1.element * (10 - $1.offset) } % 11
return sum < 2 ? check == sum : check + sum == 11
}
// Kotlin
fun isValidIranianNationalCode(input: String): Boolean {
if (input.length != 10) return false
val digits = input.mapNotNull(Char::digitToIntOrNull).takeIf { it.size == 10 } ?: return false
val check = digits[9]
val sum = digits.slice(0..8).asSequence().mapIndexed { i, x -> x * (10 - i) }.sum() % 11
return if (sum < 2) check == sum else check + sum == 11
}
// Groovy, based on @mehrali work
boolean isValidIranianNationalCode(String input) {
if (!input?.matches('^\\d{10}$'))
return false
int check = input[9].toInteger()
int sum = (0..8).sum({ input[it].toInteger() * (10 - it) }) % 11
return sum < 2 ? check == sum : check + sum == 11
}
// Rust
fn is_valid_iranian_national_code(s: &str) -> bool {
let digits: Vec<u32> = s.chars().filter_map(|x| x.to_digit(10)).collect();
if s.len() != 10 || digits.len() != 10 {
return false;
}
let check = digits[9];
let sum = (0..9).map(|x| digits[x] * (10 - x) as u32).sum::<u32>() % 11;
if sum < 2 {
check == sum
} else {
check + sum == 11
}
}
// Haskell, contributed by @hzamani
import Data.Char
isValidNationalCode code =
length code == 10 && all isDigit code && if s < 2 then check == s else check + s == 11
where
digits = map digitToInt code
check = last digits
s = sum (map (\(d, i) -> d * (10 - i)) $ digits `zip` [0..8]) `mod` 11
// Erlang, contributed by @hzamani
is_valid_national_code(Code) ->
length(Code) == 10
andalso lists:all(fun(D) -> $0 =< D andalso D =< $9 end, Code)
andalso begin
ToDigit = fun(Char) -> Char - $0 end,
S = lists:sum(lists:map(fun({I, D}) -> D * (10 - I) end, lists:zip(lists:seq(0, 8), lists:droplast(lists:map(ToDigit, Code))))) rem 11,
Check = ToDigit(lists:last(Code)),
if
S < 2 -> Check == S;
true -> Check + S == 11
end
end.
// Elixir, contributed by @hzamani
fn input ->
if input =~ ~r/^\d{10}$/ do
[check | digits] =
input
|> String.split("", trim: true)
|> Enum.map(&String.to_integer/1)
|> Enum.reverse
s =
digits
|> Enum.with_index(2)
|> Enum.map(fn {digit, i} -> digit * i end)
|> Enum.sum
|> rem(11)
if s < 2 do check == s else check + s == 11 end
else
false
end
end
// PowerQuery M Language, in a collaboration with Calak
(input as text) as logical =>
let
check = Number.From(Text.End(input, 1)),
s = Number.Mod(List.Sum(List.Transform({0..8}, each (10 - _) * Number.From(Text.At(input, _)))), 11)
in Text.Length(input) = 10 and
Text.Select(input, {"0".."9"}) = input and
(if s < 2 then check = s else check + s = 11)
// VBA, in a collaboration with Calak
Public Function IsValidIranianNationalCode(code As String) As Boolean
If Not code Like String(10, "#") Then Exit Function
Dim check As Byte, i As Byte, s As Integer
check = Mid(code, 10)
For i = 1 To 9
s = s + (11 - i) * Mid(code, i, 1)
Next i
s = s Mod 11
IsValidIranianNationalCode = IIf(s < 2, check = s, check + s = 11)
End Function
// R, by Calak
is_valid_iran_code <- function(input) {
if (!grepl('^[0-9]{10}$', input)) return (FALSE)
check <- as.integer(substr(input, 10, 10))
s <- t(10:2) %*% as.integer(unlist(strsplit(input, "")))[-10] %% 11
return (if (s < 2) check == s else check + s == 11)
}
// Lua
function is_valid_iranian_national_code(input)
if input:match(string.rep("%d", 10)) ~= input then return false end
local check = tonumber(input:sub(10, 10))
local sum = 0
for i = 1, 9 do
sum = sum + (11 - i) * tonumber(input:sub(index, index))
end
sum = sum % 11
return (sum < 2) and (check == sum) or (check + sum == 11)
end
// Fortran, by Calak
logical pure function is_valid_iranian_national_code(input)
character(10), intent(in) :: input; integer :: check, i, s
integer, parameter :: zero = ichar("0")
is_valid_iranian_national_code = .false.
if (verify(input, "0123456789") /= 0) return
check = ichar(input(10:)) - zero
s = mod(sum((/ ((11 - i) * (ichar(input(i:i)) - zero), i = 1, 9) /)), 11)
is_valid_iranian_national_code = merge(check == s, check + s == 11, s < 2)
end function is_valid_iranian_national_code
// Pascal/Delphi, by Calak
function isValidIranianNationalCode(input: string): boolean;
const s: integer = 0;
var check, i: byte;
begin
isValidIranianNationalCode := false;
if ord(input[0]) <> 10 then exit;
for i := 1 to 10 do if not (input[i] in ['0'..'9']) then exit;
val(input[10], check);
for i := 1 to 9 do inc(s, (11 - i) * (ord(input[i]) - ord('0')));
s := s mod 11;
isValidIranianNationalCode := ((s < 2) and (check = s) or (s >= 2) and (check + s = 11));
end;
// Excel 365, by Calak
= LAMBDA(input,
IF(
AND(LEN(input) = 10, ISNUMBER(--MID(input, SEQUENCE(10), 1))),
LET(
check, --RIGHT(input),
s, MOD(SUMPRODUCT(SEQUENCE(9, 1, 10, -1), --MID(input, SEQUENCE(9), 1)), 11),
IF(s < 2, check = s, check + s = 11)
)
)
)
// Stored Procedure, MySQL
DELIMITER $$
CREATE FUNCTION is_valid_iranian_national_code(input TEXT) RETURNS BOOLEAN DETERMINISTIC BEGIN
IF input NOT REGEXP '^[0-9]{10}$' THEN RETURN false; END IF;
SET @i = 1, @s = 0;
WHILE @i < 10 DO SET @s = @s + (11 - @i) * SUBSTR(input, @i, 1), @i = @i + 1; END WHILE;
SET @s = @s % 11, @check = +SUBSTR(input, -1);
RETURN IF(@s < 2, @check = @s, @check + @s = 11);
END $$
DELIMITER ;
@maysam
Copy link

maysam commented Jan 26, 2020

ممنون
درسته
خودم از همین استفاده می کنیم
برای تایپ اسکریپتش زدم

@ahbanavi
Copy link

ahbanavi commented Jan 9, 2021

Thx

@miladyousefi
Copy link

ممنون .

اسکریپتی هست که بشه احتمالشو بررسی کرد ؟

ینی مثلا یه عددی بین صفر تا یک بده بگه که چقدر این میتونه شبیه به کدملی باشه .

@ammirzaei
Copy link

ammirzaei commented Nov 26, 2021

این الگوریتم ها به یک طریق دیگه ای هم گول میخورن مثل :
1666666661
3337777333
9999229999
یعنی اعداد اول و آخر مثل هم باشن و اعداد بین هم مثل هم
در هر صورت کاربر یه راهی پیدا میکنه برای دور زدن!

@ebraminio
Copy link
Author

بله

https://web.archive.org/web/20170706081048/http://www.fardanews.com/fa/news/127747/رندترین-شماره-ملی-بلای-جان-صاحبش-شد

را حتماً بررسی کرده‌اید

یک الگوریتم صحت‌سنجی اولیه ببیشتر جنبهٔ کمک به کاربر در وارد کردن دارد و اگر کاربر قصد وارد کردن اطلاعات نادرست داشته باشد چاره‌اش بررسی با پایگاه داده‌های ملی است تا ایده‌های مثل بررسی عدم تکرار ده رقم

@ammirzaei
Copy link

ammirzaei commented Nov 26, 2021

بله، راه حل اصلی همون بررسی با دیتابیس ملی است
برای نمونه سایتی مثل دیجی کالا هم برای بررسی از همین الگوریتم ها استفاده میکند و حتی عدم تکرار ده رقم کد ملی را هم بررسی می کند اما با روشی که بالاتر گفتم گول میخورد!
اون بنده خدا تو دیجی کالا هم نمیتونه کد ملیش رو ثبت کنه!

@hassanKhademi
Copy link

سلام و ممنون
خدا خیرت بده

@ZargarAdel
Copy link

سلام و ممنون
لطفا به زبان SQL-Server هم کد را ترجمه کنید...
در پایگاه داده یک کوئری می خواهیم که کدهای ملی نامعتبر را فیلتر کند.

@ebraminio
Copy link
Author

ebraminio commented Nov 8, 2022

@ZargarAdel

به sql server دسترسی ندارم ولی برای mysql را قرار دادم، احتمالاً بتوانید آن را در sql server با کمی تغییر استفاده کنید 😊

@miladjalalli
Copy link

دمت گرم، عالی بود😊

@aghalati
Copy link

خیلی ممنون

دوستان من قصد استفاده از بخش php برای woocommerce را دارم، لطفا بفرمایید که کد زیر چه مشکلی دارد؟

`function isValidIranianNationalCode($input) {
if (!preg_match("/^\d{10}$/", $input)) {
return false;
}

$check = (int) $input[9];
$sum = array_sum(array_map(function ($x) use ($input) {
    return ((int) $input[$x]) * (10 - $x);
}, range(0, 8))) % 11;

return $sum < 2 ? $check == $sum : $check + $sum == 11;

}

add_filter( 'flexible_checkout_fields_custom_validation', 'wpdesk_fcf_custom_validation_CodeMelli' );
/**

  • Add custom URL validation

*/
function wpdesk_fcf_custom_validation_CodeMelli( $custom_validation ) {
$custom_validation['_billing_code_melli'] = array(
'label' => 'Code Melli',
'callback' => 'isValidIranianNationalCode'
);

return $custom_validation;

}

`

@ebraminio
Copy link
Author

ebraminio commented Dec 17, 2023

سلام @mbgame اعداد تکراری نامعتبر نیست https://web.archive.org/web/20170706081048/http://www.fardanews.com/fa/news/127747/رندترین-شماره-ملی-بلای-جان-صاحبش-شد قبلاً همین regular expression را داشتیم که حذف کردم و بالای این صفحه هم این موضوع رو نوشته‌ام، اگر کاربر قصد دور زدن ورود عدد را داشته باشد به هر حال راهی برای دور زدن آن پیدا می‌کند و راه‌حل، اضافه کردن قانونی ناموجود نیست

@reza-sdo
Copy link

thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment