পাইথন দিয়ে সর্টিং – ২য় পর্ব

পাইথন প্রোগ্রামিং ভাষায় সর্ট করার পদ্ধতি।

আগের পর্বে আমরা দেখেছি কীভাবে পাইথনের বিল্টইন ফাংশন ব্যবহার করে সর্টিং করা যায়। এই লেখায় আমরা আরেকটু জটিল সর্টিংয়ের কাজ করব, তবে বিল্টইন ফাংশন ব্যবহার করেই কাজগুলো করা হবে।

ধরা যাক, একটি লিস্টে বিভিন্ন ফলের নাম এবং সেই ফল কতগুলো করে আছে, সেটি দেওয়া আছে – fruits = [(‘orange’, 3), (‘apple’, 3), (‘banana’, 2), (‘mango’, 10), (‘guava’, 5)]

এখন এই লিস্টকে আমরা যদি সর্ট করি, তাহলে ফলের নাম অনুসারে সর্ট হয়ে যাবে –

>>> fruits = [('orange', 3), ('apple', 3), ('banana', 2), ('mango', 10), ('guava', 5)]
>>> print(sorted(fruits))
[('apple', 3), ('banana', 2), ('guava', 5), ('mango', 10), ('orange', 3)]

ফলের নাম অনুসারে সর্ট হওয়ার কারণ কী? ফলের সংখ্যা অনুসারেও তো সর্ট হতে পারত। এখানে প্রতিটি টাপলের প্রথম উপাদান সেই টাপলের প্রতিনিধিত্ব করে। যেমন, টাপলে আমরা যদি আগে সংখ্যা লিখতাম, তারপরে ফলের নাম লিখতাম, তাহলে সংখ্যা অনুযায়ী সর্ট হতো।

কিন্তু আমরা যদি চাই, আমাদেরকে যেই লিস্ট দেওয়া আছে সেটি ফলের নাম নয়, বরং সংখ্যা অনুসারে সর্ট করা হবে, তখন কী করতে হবে? প্রতিটি টাপলের দ্বিতীয় উপাদানটি যদি সেই টাপলের প্রতিনিধিত্ব করত, তাহলে কিন্তু আমরা কাঙ্ক্ষিত উপায়ে সর্ট করতে পারতাম। sorted() ফাংশনে key নামে একটি প্যারামিটার আছে, যার মাধ্যমে আমরা বলে দিতে পারি, কোন উপাদানটির ওপর ভিত্তি করে সর্ট করার কাজটি হবে। key-তে আসলে একটি ফাংশন দেওয়া হয়, আর যেই লিস্ট সর্ট করতে হবে, তার প্রতিটি উপাদান সেই ফাংশনের মধ্যে পাঠানো হয়। ফাংশনটি একটি উপাদান রিটার্ন করবে, যার ওপর ভিত্তি করে সর্টিং হবে। তাহলে আমরা এখানে যেই কাজটি করতে চাচ্ছি, সেখানে এমন একটি ফাংশন লিখতে হবে, যা (‘apple’, 3) ইনপুট নিবে আর 3 রিটার্ন করবে।

def compare_fnc(item):
    return item[1]

fruits = [('orange', 3), ('apple', 3), ('banana', 2), ('mango', 10), ('guava', 5)]
print(sorted(fruits, key=compare_fnc))

ওপরের কোড রান করলে দেখা যাবে ফলের সংখ্যা অনুযায়ী ছোট থেকে বড় ক্রমে সর্ট করা হয়ে গিয়েছে।

[('banana', 2), ('orange', 3), ('apple', 3), ('guava', 5), ('mango', 10)]

বড় থেকে ছোট ক্রমে সর্ট করতে চাইলে লিখতে হবে sorted(fruits, key=compare_fnc, reverse=True).

পাইথনে operator মডিউলে একটি ফাংশন আছে itemgetter, যেটি ব্যবহার করে আমরা ওপরের কাজটি আরো সহজে করতে পারি, আমাদের নিজেদের কষ্ট করে ফাংশন তৈরি করতে হবে না।

from operator import itemgetter

fruits = [('orange', 3), ('apple', 3), ('banana', 2), ('mango', 10), ('guava', 5)]
print(sorted(fruits, key=itemgetter(1)))

ওপরের কোডে itemgetter(1) এর বদলে itemgetter(0) লিখলে ফলের নাম অনুযায়ী সর্ট হয়ে যাবে। এখন আমরা যদি চাই, প্রথমে ফলের সংখ্যা অনুযায়ী সর্ট হবে, তারপরে যেসব ফলের সংখ্যা সমান, তাদের মধ্যে নাম অনুযায়ী সর্ট হবে, তাহলে কী করতে হবে? মানে আমাদের আউটপুট (‘orange’, 3′), (‘apple’, 3) ক্রমে না এসে (‘apple’, 3), (‘orange’, 3) ক্রমে আসবে। কাজটি সহজেই করা যায় এভাবে –

>>> fruits = [('orange', 3), ('apple', 3), ('banana', 2), ('mango', 10), ('guava', 5)]
>>> print(sorted(fruits, key=itemgetter(1, 0)))
[('banana', 2), ('apple', 3), ('orange', 3), ('guava', 5), ('mango', 10)]

এখান আমরা itemgetter(1, 0) ব্যবহার করেছি। কিন্তু এখন আমরা যদি চাই, ফলের সংখ্যার বড় থেকে ছোট ক্রমে সর্ট হবে আর যেসব ফলের সংখ্যা সমান, তারা নাম অনুযায়ী ছোট থেকে বড় ক্রমে সর্ট হবে, তখন কী করতে হবে? তাহলে দুইবার সর্ট করার কাজটি করতে হবে –

>>> fruits = [('orange', 3), ('apple', 3), ('banana', 2), ('mango', 10), ('guava', 5)]
>>> print(fruits)
[('orange', 3), ('apple', 3), ('banana', 2), ('mango', 10), ('guava', 5)]
>>> fruits = sorted(fruits, key=itemgetter(0))
>>> print(fruits)
[('apple', 3), ('banana', 2), ('guava', 5), ('mango', 10), ('orange', 3)]
>>> fruits = sorted(fruits, key=itemgetter(1), reverse=True)
>>> print(fruits)
[('mango', 10), ('guava', 5), ('apple', 3), ('orange', 3), ('banana', 2)]

ওপরের পদ্ধতি কাজ করে, কারণ পাইথনের sorted() ফাংশন stable সর্ট করে। sort()-এর ক্ষেত্রেও একই কথা প্রযোজ্য।