Click here to Skip to main content
Click here to Skip to main content

Tagged as

Position & Value Based Indexers

, 9 Jun 2014
Rate this:
Please Sign up or sign in to vote.
This article is for beginners to know how to implement Position based and Value based Indexers

Introduction

We all know that Array is nothing but sequential memory locations in which data are stored. Let us say the size of continuous memory location is 80 KB and size of one unit of data is 2 KB. The statement implies that we have an array of 40 data in sequential memory location when each data consumes 2KB in size. The below picture shows the above explanation:

Indexers_Pos_Value/Pic1.JPG

For example, consider the below array:

Department dpt = new Department[40];

If we assume the size required to store each department is 2 KB, we have 40 blocks of data, each consuming 2KB, allocated to accommodate 40 department objects. Also note that 40 objects are allocated in sequential order. So, how do we get the object at the third memory block? We use the below statement: Dpt[2]. What does [2] represent here? It says to take the object from the third memory block. So here, each memory block is referred by the Indexed location. So the notation [] is what called Indexer.

In this article, we will see how we can implement a Simple position based Indexer and Value based for our collection class. Ok, let us start.

The Product Class

Consider the below specified simple class which represents the product for a retail shop. It has two private data members; a constructor and a public method to retrieve the product name.

//001: Product Class.
public class Product
{
	private int ProductId;
	private string ProductName;

	public Product(int id, string Name)
	{
		ProductId = id;
		ProductName = Name;
	}

	public string GetProdName()
	{
		return ProductName;
	}
}

The SuperMarket Class

As every Supermarket has a collection of products, this class is going to have a collection of product objects. The members of this class are shown below:

//002: SuperMarket has collection of products. It implements Indexers.
public class SuperMarketX
{
	//002_1: Declaration
	private int pos;
	private Product[] Products;

Pos is to iterate through the Products collection. OK, you may have got the idea now. SuperMarket is user defined (defined by us now) collection of Products.

The constructor of this class will take an array of products as parameter and assign it to the private member Products. Note that for this article, I am allocating fixed space 1000 and each space has null reference initially. We will replace the null reference with the passed in array of objects. Below is the code for the constructor:

//002_2: Constructor
public SuperMarketX(string shopname, params Product[] products)
{
	//002_2.1: Allocate the Space required
	this.Products = new Product[1000];
	pos = 0;

	//002_2.2: first set null to all the elements
	for (int i=0; i< 1000; i++)
		Products[i] = null;

	//002_2.3: Assign the Array by taking the references from incoming array.
	//The reference will replace the previous null assignment
	foreach (Product prd in products)
	{
		Products[pos] = prd;
		pos++;
	}
	this.shopname = shopname;
}

We have overridden the ToString() method to get the entire product in comma-separated format. The method implementation is shown below:

//004: Override the ToString to display all the Product Names as Comma Separated List
public override string ToString()
{
	string returnval = "";
	foreach (Product p in Products )
	{
		if ( p != null )
			returnval = returnval + "," + p.GetProdName();
	}
	//Cut the leading "," and return
	return returnval.Substring(1, returnval.Length-1 );
}

[A] Simple Position based Indexer for SuperMarket Class

The indexer is implemented just like the overloaded functions. To implement the [] notation, follow the below syntax:

public <returntype>this[<datatype> index]

Example:

public Product this[int index]

Get an integer index from the caller give back a Product.

The Implementation Skeleton on the Simple Indexer is shown below:

Indexers_Pos_Value/Pic2.JPG

In our case, we will implement the Index for the Supermarket. So using the Positional Index, a product will be retrieved. The way the index implemented will give a null reference to caller when index is out of Range, say below 0 or above 1000 [This is the Maximum product supported by the super market]. Below is the function implementation:

//003: The Use of Indexer. Positional Indexer
public Product this[int index]
{
	get
	{
		//003_1: Retrieve value based on positional index
		if (index >= Products.Length || index < 0 )
		{
			return null;
		}
		return Products[index];
	}
	set
	{
		//003_2: Set the value based on the positional index
		if (index >= Products.Length )
		{
			return;
		}
		Products[index] = value ;
	}
}

Indexer Usage

Have a look at the below code:

//Client 001: First Let us create an array to hold 6 Products.
Product[] theProdArray = new Product[6];

//Client 002: Create 6 individual Product and store it in the array
theProdArray[0] = new Product(1001, "Beer");
theProdArray[1] = new Product(1002, "Soda");
theProdArray[2] = new Product(1003, "Tea");
theProdArray[3] = new Product(1004, "Coffee");
theProdArray[4] = new Product(1005, "Apple");
theProdArray[5] = new Product(1006, "Grapes");

//Client 003: Super Market that holds six product collection
SuperMarketX market = new SuperMarketX("Z Stores", theProdArray);
Console.WriteLine("Product Available in Super Market: " + market );

//Client 004: Use the Simple Indexer to Assign the value
market[15] = new Product(1015, "Orange");
Console.WriteLine("Product Available in Super Market: " + market );

//Client 005: Use the Simple Indexer to retrieve the value
Product prod = market[5];
Console.WriteLine("The product retrieved is: " + prod.GetProdName() );

Below is the explanation:

  • Client 001: Creates the array of 6 Products.
  • Client 002: Populates the products array. [In the real world, Array will be populated from Database.]
  • Client 003: SuperMarket is created with 6 New Products. Note that our super market capacity is 1000.
  • Client 004: Uses the Indexer to add new product to the Products collection. market[15] = new Product(1015, "Orange"); Will call the indexer with index = 15. new Product(1015, "Orange"); will be referred in the set portion of the Indexer using the value keyword.
  • Client 005: Product prod = market[5]; SuperMarket Object accessed with Indexer [5]. We will move to get portion of the Indexer and indexer returns Product at the position offset 5. The return object reference is assigned to prod.

[B] Simple Value based Indexer for SuperMarket Class

The previous indexer locates the memory block based on the Index by calculating the offset, as it knows the size of the memory block. Now we will implement value-based index; that is we will get the product based on the ProductId value. I will walk through the changes done on the Classes.

  1. The product class changed to have a setter method for ProductName, and a getter method for ProductId and an overridden method for ToString just to print Product Name. Below are the changes:
    public override string ToString()
    {
    	return ProductName;
    }
    
    public int GetProductId()
    {
    	return ProductId;
    }
    
    public void SetProductName(string newName)
    {
    	ProductName = newName;
    }
  2. In the SuperMarketX class, a variable called numeric_index_mode is declared. This variable is used to decide the Indexer is referred in Positional Mode or in Value mode.
    public int numeric_index_mode; //0-Position based index. 1-Value based Index.

    Inside the constructor, indexer mode is initialized to 0. That means SuperMarket class by default treats the Indexer as Positional indexer and retrieves the product based on the positional offset calculated.

    numeric_index_mode = 0;
  3. A public function is implemented to retrieve the Positional index for the passed-in Product Id. Note, that the product id is unique for this Value based Index. The function will iterate through the Products in the SuperMarket and return when Product Id matched. It will return –1 when match did not occur. Below is the new function implemented to support the value-based index:
    //005: Supporting function for value based Index
    public int GetProduct(int Productid)
    {
    	for (int i = 0; i < Products.Length; i++)
    	{
    		Product p = Products[i];
    		if ( p != null )
    		{
    			int prodid = p.GetProductId();
    			if (prodid == Productid)
    				return i;
    		}
    	}
        return -1;
    }

    Now Let's Go and Change the Indexer

  4. First in the get portion of the Indexer, wrap the existing code with an if construct. That is; when the Mode = 0, go with positional Index. It holds true for Set portion of the Indexer also. Below is the change:
    get
    {
    	//003_1: Retrieve Product based on positional index
    	if (numeric_index_mode == 0)
    	{
    		if (index >= Products.Length || index < 0 )
    		{
    			return null;
    		}
    		return Products[index];
    	}
    
    set
    {
    	//003_2: Set the value based on the positional index
    	if (numeric_index_mode == 0 )
    	{
    		if (index >= Products.Length )
    		{
    			return;
    		}
    		Products[index] = value ;
    	}
  5. If we are in Value mode, in the Get part of the indexer, first get the positional index for the given product id. Once we have the positional index, we are ready to make recursive call to the same indexer routine. Make sure to set the indexer mode to 0 as we need to access the indexer to get the product based on the indexed position. Once we have the Product, reset index mode back to 1; that reset indexer mode to value based as the client code would expect that. Below is the code for get portion:
    //003_2: Retrieve Product based on the Unique product Id
    if(numeric_index_mode == 1)
    {
    	int idx = GetProduct(index);
    	if (idx == -1)
    		return null;
    	else
    	{
    		//Key statement to avoid invalid recursion
    		numeric_index_mode = 0;
    		Product ret_Product = this[idx]; //Recursive call to Indexer
    		numeric_index_mode = 1; //Reset it back to user preference
    		return ret_Product;
    	}
    }
  6. Set portion of the Indexer also changed in the same way. I hope further explanation is not required:
    //003_3: Set the value based on the Id Passed in.
    if(numeric_index_mode == 1)
    {
    	int idx = GetProduct(index);
    	if (idx == -1)
    		return ;
    	else
    	{
    		//Key statement to avoid invalid recursion
    		numeric_index_mode = 0;
    		Products[idx] = value;
    		numeric_index_mode = 1; //Reset it back to user preference
    	}
    }

Usage of Value based Indexer

The code below explains how we can switch from Position based indexer to Value based indexer, use value based indexer and go back to default indexer mode. The commented code is self-explanatory. Hence no more explanation has been given.

//=====> Value based Index     <=======
//Now we will operate on the Value based Index
market.numeric_index_mode = 1;
//Client 006: Display name of the product whose product id is 1005
Console.WriteLine("Name of the Product represented by Id 1005 is: {0}", market[1016]);

//Client 007: The aim is Replace the Product Soda with Iced Soda and
//maintain same product id. The Id of Soda is 1002.
if (market[1002] != null )
{
	market[1002].SetProductName("Iced Soda");
	Console.WriteLine("Product Available in Super Market: " + market );
}

//Client 008: Remove Tea and Add French Coffee. Note the Object
//in the Indexed location will be changed.
//Note: Here check for the null is not required. Kind of Modify on fail Add
market[1003] = new Product(1007, "French Coffee");
Console.WriteLine("Product Available in Super Market: " + market );

//======> Reset back to Standard Positional Index    <=================
market.numeric_index_mode = 0;

Closing Notes

  1. You can implement string value based indexer also. The skeleton is:
    public Product this[string ProductName]
    {
    Set{}
    Get{}
    }
  2. As our positional based index takes the index as integer, we introduced numeric_index_mode. If I implement string-based index, the mode switching is not required. The alternate implementation for the Value based product id is convert the product id into string and use string based index.

Attachments

  • Indexer_src: Source code for indexer
  • Indexer_exe: Executable for Indexer. Run it in the command prompt. Map the output with the below client code:
class ProgramEntry
{
	[STAThread]
	static void Main(string[] args)
	{
		//Client 001: First Let us create an array to hold 6 Products.
		Product[] theProdArray = new Product[6];

		//Client 002: Create 6 individual Product and store it in the array
		theProdArray[0] = new Product(1001, "Beer");
		theProdArray[1] = new Product(1002, "Soda");
		theProdArray[2] = new Product(1003, "Tea");
		theProdArray[3] = new Product(1004, "Coffee");
		theProdArray[4] = new Product(1005, "Apple");
		theProdArray[5] = new Product(1006, "Grapes");

		//Client 003: Super Market that holds six product collection
		SuperMarketX market = new SuperMarketX("Z Stores", theProdArray);
		Console.WriteLine("Product Available in Super Market: " + market );

		//Client 004: Use the Simple Indexer to Assign the value
		market[15] = new Product(1015, "Orange");
		Console.WriteLine("Product Available in Super Market: " + market );

		//Client 005: Use the Simple Indexer to retrieve the value
		Product prod = market[5];
		Console.WriteLine("The product retrieved is: " + prod.GetProdName() );

		//=====> Value based Index     <=======
		//Now we will operate on the Value based Index
		market.numeric_index_mode = 1;
		//Client 006: Display name of the product whose product id is 1005
		Console.WriteLine("Name of the Product represented by Id 1005 is:
			{0}", market[1016]);

		//Client 007: The aim is Replace the Product Soda with
                  //Iced Soda and maintain same product id. The Id of Soda is 1002.
		if (market[1002] != null )
		{
			market[1002].SetProductName("Iced Soda");
			Console.WriteLine("Product Available in Super Market:
				" + market );
		}

		//Client 008: Remove Tea and Add French Coffee.
		//Note the Object in the Indexed location will be changed.
		//Note: Here check for the null is not required.
		//Kind of Modify on fail Add
		market[1003] = new Product(1007, "French Coffee");
		Console.WriteLine("Product Available in Super Market: " + market );

		//======> Reset back to Standard Positional Index    <=================
		market.numeric_index_mode = 0;
        //Dot
	}
}

History

  • 31st October, 2010: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Sivaraman Dhamodharan
Software Developer iSOFT
India India
I am working as software engineer in iSOFT R&D. I have been come accross C++,MFC, .net technologies. I do like playing video games, reading books.
 

Web: www.mstecharticles.com


Comments and Discussions

 
GeneralMy vote of 4 PinprofessionalAmir Mohammad Nasrollahi13-Aug-13 9:29 
GeneralMy vote of 1 PinmemberTobiasP13-Feb-12 2:13 
GeneralI must have missed something... [modified] PinmemberGalatei2-Nov-10 10:16 
GeneralRe: I must have missed something... Pinmembersirama20043-Nov-10 6:36 
General[My vote of 1] not thread safe and... PinmemberSeishin#31-Oct-10 22:34 
GeneralRe: [My vote of 1] not thread safe and... [modified] Pinmembersirama20041-Nov-10 2:50 
GeneralMy vote of 4 PinmemberPeteSon_Q31-Oct-10 4:08 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140827.1 | Last Updated 9 Jun 2014
Article Copyright 2010 by Sivaraman Dhamodharan
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid