C# - LINQ - Basic Set Theory
Overview
This example focuses on basic set theory. The .Net Framework has several applicable LINQ extension methods, which can be easily used for determining four functions in basic set theory. These are...
- Unions
- Intersections
- Complements
- Cartesian Products
This is a simple application requiring minimal coding. The code is annotated throughout, with the Operations Class having more detailed annotations.
The SetInput UserControl
This control encapsulates adding to or removing from a set of text items. It has a method to clear all of your input, and a function that returns your set as an array of Strings. This is a neat way for dealing with sets as a UserControl acts as an information specialist and avoids duplicating code.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Set_Theory_cs
{
public partial class SetInput : UserControl
{
//custom event
public delegate void contentChangedEventHandler();
public event contentChangedEventHandler contentChanged;
private ContextMenuStrip cms = new ContextMenuStrip();
private ErrorProvider inputErrorProvider = new ErrorProvider();
//sets label text
public string SetID
{
set
{
lblTitle.Text = "Set " + value;
}
}
//returns set size
public int setLength
{
get
{
return lstDisplay.Items.Count;
}
}
//Constructor...sets up the ContextMenuStrip for the ListBox
public SetInput()
{
InitializeComponent();
cms.Items.Add("Remove item", null, removeItem);
if (contentChanged != null) {contentChanged();}
this.Paint += SetInput_Paint;
txtInput.KeyDown += txtInput_KeyDown;
}
//paints title background
private void SetInput_Paint(object sender, PaintEventArgs e)
{
e.Graphics.FillRectangle(SystemBrushes.ControlDark, new Rectangle(0, 0, this.Width, 23));
}
//tests if item exists in set, if not it adds it to the set
private void btnAdd_Click(object sender, EventArgs e)
{
if (lstDisplay.Items.Contains(txtInput.Text))
{
inputErrorProvider.SetError(txtInput, "Set contains '" + txtInput.Text + "'");
}
else
{
lstDisplay.Items.Add(txtInput.Text);
if (contentChanged != null) { contentChanged();}
txtInput.Clear();
}
}
//clears the ErrorProvider
private void txtInput_TextChanged(object sender, EventArgs e)
{
inputErrorProvider.Clear();
}
//enables Enter-Button press
private void txtInput_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter) {btnAdd.PerformClick();}
}
//only shows ContextMenuStrip if an item is selected in the ListBox
private void lstDisplay_SelectedIndexChanged(object sender, EventArgs e)
{
if (lstDisplay.SelectedIndex != -1)
{
lstDisplay.ContextMenuStrip = cms;
}
else
{
lstDisplay.ContextMenuStrip = null;
}
}
//MenuItem click eventhandler. Removes list item
private void removeItem(object sender, EventArgs e)
{
if (lstDisplay.SelectedIndex != -1)
{
lstDisplay.Items.RemoveAt(lstDisplay.SelectedIndex);
if (contentChanged != null) {contentChanged();}
}
}
//Control clear method. Clears any input
public void clear()
{
inputErrorProvider.Clear();
lstDisplay.Items.Clear();
txtInput.Clear();
}
//Returns set items
public string[] getSet()
{
return lstDisplay.Items.Cast<string>().ToArray();
}
}
}
The Form
The Form is the container for the GUI, which consists of two SetInput UserControls, a ComboBox, a ListBox, and two Buttons. The Form code is fairly simple and annotated throughout.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Set_Theory_cs
{
public partial class frmExample : Form
{
Dictionary<int, Delegate> functions = new Dictionary<int, Delegate>();
Operations operations = new Operations();
//adds some handlers
public frmExample()
{
InitializeComponent();
setInput1.contentChanged += SetInputs_contentChanged;
setInput2.contentChanged += SetInputs_contentChanged;
}
//app initializing
private void frmExample_Load(object sender, EventArgs e)
{
setInput1.SetID = "A";
setInput2.SetID = "B";
cboChoice.SelectedIndex = 0;
functions.Add(1, new Func<string[], string[], string[]>(operations.union));
functions.Add(2, new Func<string[], string[], string[]>(operations.intersection));
functions.Add(3, new Func<string[], string[], string[]>(operations.complement));
functions.Add(4, new Func<string[], string[], string[]>(operations.cartesianProduct));
}
//clears any input
private void btnClear_Click(object sender, EventArgs e)
{
setInput1.clear();
setInput2.clear();
if (cboChoice.SelectedIndex > 0) {lstResults.Items.Clear();}
setEnabled();
}
//changes btnClear.Enabled if necessary
private void setEnabled()
{
btnClear.Enabled = setInput1.setLength + setInput2.setLength + lstResults.Items.Count + ((cboChoice.SelectedIndex == 0) ? -1 : 0) > 0;
}
//adds results from selected function to lstResults
private void btnCompute_Click(object sender, EventArgs e)
{
lstResults.Items.Clear();
lstResults.Items.AddRange((string[])(functions[cboChoice.SelectedIndex].DynamicInvoke(setInput1.getSet(), setInput2.getSet())));
setEnabled();
}
//sets some gui properties
private void cboChoice_SelectedIndexChanged(object sender, EventArgs e)
{
lstResults.Items.Clear();
if (cboChoice.SelectedIndex == 0) {lstResults.Items.Add("No operation selected");}
btnCompute.Enabled = cboChoice.SelectedIndex > 0;
}
//Calls setEnabled(), which changes btnClear.Enabled if necessary
private void SetInputs_contentChanged()
{
setEnabled();
}
}
}
The Operations Class
This Class contains just four functions, all of which take two String array arguments, and return a String array. These four functions correspond to the four set theory functions mentioned in the overview. Using LINQ, these really are one-liners, but they produce the required results.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Set_Theory_cs
{
public class Operations
{
//error message
private string[] errortext = new string[] {"Insufficient data"};
/// <summary>
/// union Function
/// </summary>
/// <param name="setA"></param>
/// <param name="setB"></param>
/// <returns>with two inputs {1, 2} and {2, 3} returns {1, 2, 3}</returns>
public string[] union(string[] setA, string[] setB)
{
return ((setA.Count() != 0 | setB.Count() != 0) ? setA.Union(setB).ToArray() : errortext);
}
/// <summary>
/// intersection Function
/// </summary>
/// <param name="setA"></param>
/// <param name="setB"></param>
/// <returns>with two inputs {1, 2} and {2, 3} returns {2} as both arrays contain 2</returns>
public string[] intersection(string[] setA, string[] setB)
{
return ((setA.Count() != 0 & setB.Count() != 0) ? setA.Intersect(setB).ToArray() : errortext);
}
/// <summary>
/// complement Function
/// </summary>
/// <param name="setA"></param>
/// <param name="setB"></param>
/// <returns>with two inputs {1, 2} and {2, 3} returns {1} as 1 is the only
/// number in array1 that doesn't exist in array2</returns>
public string[] complement(string[] setA, string[] setB)
{
return ((setA.Count() > 0 & setB.Count() != 0) ? setA.Except(setB).ToArray() : errortext);
}
/// <summary>
/// cartesianProduct Function
/// </summary>
/// <param name="setA"></param>
/// <param name="setB"></param>
/// <returns>with two inputs {1, 2} and {apple, orange} returns {(1, apple), (1, orange), (2, apple), (2, orange)}</returns>
public string[] cartesianProduct(string[] setA, string[] setB)
{
return ((setA.Count() != 0 & setB.Count() != 0) ? setA.SelectMany((x) => setB.Select((y) => "(" + x + ", " + y + ")")).ToArray() : errortext);
}
}
}
Conclusion
This example shows some LINQ methods, but more importantly, shows how to create reusable UserControls for data input, editing, and display.