Skip to content

Instantly share code, notes, and snippets.

@ebraminio
Last active March 11, 2024 14:34
Star You must be signed in to star a gist
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 ;
@Elias-Heydarpour
Copy link

thanks,
very good!

@koctodox
Copy link

koctodox commented Feb 9, 2018

You are hero !!!

@alireza267
Copy link

Excellent! thanks a lot.

@Mhs-220
Copy link

Mhs-220 commented Apr 10, 2018

Thank You! it's very helpful

@hamed-bolhasani
Copy link

سپاسگزار از زحمات شما، عالی بود

@ebraminio
Copy link
Author

بعد از سال‌ها این اسکریپت دوباره به کارم آمد و نسخهٔ کاتلینش را اضافه کردم :)

@vmohir
Copy link

vmohir commented Feb 20, 2019

In ES6, the fill method needs has 1-3 arguments. you need to change Array(9).fill() to Array(9).fill(undefined).
Or I prefer to use this code instead:

Array.from(new Array(9), (_, i) => +input[i] * (10 - i)).reduce((x, y) => x + y) % 11

@ebraminio
Copy link
Author

@dehghani-mehdi @iMojtaba @aghasoroush توضیح بالای کد را یک بررسی مجدد کنید، ممنونم

@ebraminio
Copy link
Author

@mehrali @sparse91 اعمال شد :)

@mahdigh99
Copy link

ورژن جاوااسکریپت کار نمیکنه! هر چی میدی غلط تشخیص میده حتی ۱۱۱۱۱۱۱۱۱۱

@ebraminio
Copy link
Author

ebraminio commented Aug 5, 2019

لطفاً از نوع رشته ارسال کنید چون بعضی از شماره ملی‌ها دارای صفر در اول هستند و اعداد به نظر ورودی درستی نباشن برای همچین کاری، ممنون

@mahdigh99
Copy link

ممنونم
توی کنسول حل شد اما توی پروژم به طرز عجیبی همچنان نمیشه
مشکل هم از همون تعداد اوله با اینکه دقیقا تعداد ارقامش ۱۰ تاست ولی بازم فالس میده

@mahdigh99
Copy link

mahdigh99 commented Aug 6, 2019

مشکلم حل شد
مشکل از اونجا بود که وقتی از
template engine
استفاده میکنی مرورگر بک اسلش ها رو اسکیپ میکنه
باید با همچین حالتی بنویسی تا اوکی بشه
/^\\d{10}$/

if (!/^\d{10}$/.test(input)) ===> if (!/^\\d{10}$/.test(input))

@ebraminio
Copy link
Author

ebraminio commented Aug 17, 2019

این کد اصلی این صفحه نیست، شرط اعداد برابر نادرست است (و این بدترین شیوهٔ بررسی آن است، می‌توان آن را در همان عبارت باقاعدهٔ اصلی چا کرد) و در بالای صفحهٔ توضیح داده شده که چرا، اگر کاربرتان نمی‌خواهد اعداد واقعی دهد چه این شرط را بگذارید چه نه به شما اعداد واقعی نخواهد داد و آخر راهی برای دادن اعداد اشتباه پیدا خواهد کرد

@masoudlotfi
Copy link

masoudlotfi commented Dec 7, 2019

Hi Ebrahim.
Thanks for share this code with us.But it has one problem for java code.
I run my test and i found this problem.
For nationalCode = ''0000000000", "1111111111" this code return True But should return false.

@ebraminio
Copy link
Author

Why it should, it was doing but removed after http://www.fardanews.com/fa/news/127747 years ago

@ebraminio
Copy link
Author

سلام گرامی خواستید دو کد جاوااسکریپت موجود در همین صفحه که همین کار را با تعداد کمتری دستور و
http://www.fardanews.com/fa/news/127747
این خبر را هم نگاهی بیاندازید، عملکرد پد
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padStart
هم مدت خوبی است به مرورگرها اضافه شده خواستید می‌توانید از آن استفاده کنید، ممنونم

@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;

}

`

@mbgame
Copy link

mbgame commented Dec 16, 2023

برای جاوااسکریپتش کد شما رو کمی تغییر دادم : (جهت عدم تکرار اعداد)

function validateIranianNationalId(input) {
let regex = /^(?!(\d)\1{9})\d{10}$/;
if (!regex.test(input))
return false;

let check = +input[9];
let sum = Array.from(input.substr(0, 9), Number)
.reduce((acc, curr, i) => acc + curr * (10 - i), 0) % 11;

return (sum < 2 && check == sum) || (sum >= 2 && check + sum == 11);
}

@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 را داشتیم که حذف کردم و بالای این صفحه هم این موضوع رو نوشته‌ام، اگر کاربر قصد دور زدن ورود عدد را داشته باشد به هر حال راهی برای دور زدن آن پیدا می‌کند و راه‌حل، اضافه کردن قانونی ناموجود نیست

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