Finding 'pairs' in a List - c#

I have a list of Buttons and I want to get the two elements that are 'equal':
for (int i = 0; i < Memory.Count; i++ )
{
piezas = Memory.FindAll(s => (s.Name != Memory[i].Name && Utilidades.CompareImage(s.Image, Memory[i].Image)));
}
This is supposed (if I'm not wrong) the list with the two elements that have different Name but are using the same Image. I'm more than sure that such elements exists... but I don't know why this doesn't work.
"Utilidades.CompareImage" is an static method:
public static bool CompareImage(Image firstImage, Image secondImage)
{
MemoryStream ms = new MemoryStream();
firstImage.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
String firstBitmap = Convert.ToBase64String(ms.ToArray());
ms.Position = 0;
secondImage.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
String secondBitmap = Convert.ToBase64String(ms.ToArray());
if (firstBitmap.Equals(secondBitmap))
return true;
else
return false;
}
I've tested the method before and it's working as intented.
Can you please help me?

The code
for (int i = 0; i < Memory.Count; i++ )
{
var piezas = Memory.FindAll(s => (s.Name != Memory[i].Name
&& Utilidades.CompareImage(s.Image, Memory[i].Image)));
}
creates a new 'piezas' every time and then doesn't use it.
Also you are checking all combinations twice.
So do you want to find all pairs, the first pair or maybe something in between?
A quick fix, but far from perfect,
for (int i = 0; i < Memory.Count; i++ )
{
var piezas = Memory.FindAll(s => (s.Name != Memory[i].Name
&& Utilidades.CompareImage(s.Image, Memory[i].Image)));
if (piezas.Count > 0)
{
// use piezas[0] somehow
break;
}
}

It seems no one mentioned SelectMany yet:
var pieza = Memory.SelectMany((m, i) =>
Memory.Where((m2, j) => i < j && m.Name != m2.Name &&
Utilidades.CompareImage(m.Image, m2.Image))
.Select(m2 => Tuple.Create(m, m2))).First();
if you want all pairs, not just one, replace the final First() call to ToList() or something like that.

Related

Iterating through a list is very slow, how to improve the performance

I have a method that iterate through a list and display the result of each type one followed by another meaning that I will display result from rd followed by result from cv followed by adz. But the actual method is very slow and it takes long time to retrieve back the results. what are the ways to improve the performance and will it matter using different data structure
private List<AllJobModel> GetAllJobModelsOrder(List<AllJobModel> result)
{
var countItems = result.Count;
List<AllJobModel> list = new List<AllJobModel>();
while (countItems != 0)
{
for (int i = 0; i < countItems; i++)
{
if (result.ElementAt(i).JobImage.Contains("rd"))
{
list.Add(result.ElementAt(i));
result.RemoveAt(i);
countItems--;
break;
}
}
for (int i = 0; i < countItems; i++)
{
if (result.ElementAt(i).JobImage.Contains("cv"))
{
list.Add(result.ElementAt(i));
result.RemoveAt(i);
countItems--;
break;
}
}
for (int i = 0; i < countItems; i++)
{
if (result.ElementAt(i).JobImage.Contains("adz"))
{
list.Add(result.ElementAt(i));
result.RemoveAt(i);
countItems--;
break;
}
}
for(int i =0; i < countItems; i++)
{
if((!result.ElementAt(i).JobImage.StartsWith("rd") && !result.ElementAt(i).JobImage.StartsWith("adz")) && !result.ElementAt(i).JobImage.StartsWith("cv"))
{
list.Add(result.ElementAt(i));
result.RemoveAt(i);
countItems--;
break;
}
}
}
return list;
}
This is a bad way to sort. You're effectively looping over the list 4 times, extracting specific groups of items each time. You also have a potential bug by removing items as you are iterating over the list. While there are many improvements you can make to your method, a better way would be to use OrderBy with the following sort condition:
list = result.OrderBy( m => m.JobImage.Contains("rd") ? 1 :
m.JobImage.Contains("cv") ? 2 :
m.JobImage.Contains("adz") ? 3 :
4)
.ToList();
Check out this link: https://cc.davelozinski.com/c-sharp/fastest-collection-for-string-lookups
Was very helpfull for me.
Regarding your specific problem. It looks like you're doing some filtering of the data. Linq has an easy way of doing that. I haven't test performance but if you can give me some example data ill try it for you. It could be something like this.
private List<AllJobModel> GetAllJobModelsOrder(List<AllJobModel> result)
{
return result.Where(x => x.JobImage.Contains("rd") || x.JobImage.Contains("cv") || x.JobImage.Contains("adz")).ToList();
}

Writing text with a for cycle

for (int i = 0; i<count; i++)
{
if (Herojai[i].Jėga == Herojai[i].Intelektas || Herojai[i].Jėga == Herojai[i].Vikrumas || Herojai[i].Intelektas == Herojai[i].Vikrumas)
{
string text = Herojai[i].Vardas; // (the thing I want to write)
File.WriteAllText(#"xxx.csv", text);
}
}
With this code, every loop where the if gets passed, the text gets overwritten for new one. I don't know how to write a new line in the CSV every loop.
var items = Herojai.Where(i => i.Jėga == i.Intelektas || i.Jėga == i.Vikrumas || i.Intelektas == i.Vikrumas)
.Select(i => i.Vardas);
using (var sw = new StreamWriter(#"xxx.csv"))
{
foreaach(var item in items)
{
sw.WriteLine(item);
}
}
In your case, I would use StringBuilder class to connected string values in If condition, then let all content write to a file.
StringBuilder sb = new StringBuilder();
for (int i = 0; i < count; i++)
{
if (Herojai[i].Jėga == Herojai[i].Intelektas || Herojai[i].Jėga == Herojai[i].Vikrumas || Herojai[i].Intelektas == Herojai[i].Vikrumas)
{
sb.Append(Herojai[i].Vardas);
}
}
File.WriteAllText(#"xxx.csv", sb.ToString());
Use AppendAllText method:
File.AppendAllText(#"xxx.csv", text + Environment.NewLine);
Instead of:
File.WriteAllText(#"xxx.csv", text);
File.WriteAllText will open a file, write the text, and then close it. It will overwrite whatever is already in the file.

AddListener in foreach

Suddenly problem with lambda in foreach statement:
IEnumerator RefreshNextFrame( Part Current )
{
yield return null;
if( Current.Nodes == null )
yield break;
Current.Nodes.ForEach( n => Debug.Log( n.name ) );//outputs node0, node1
for( int i = 0; i < Current.Nodes.Count; i++ )
{
Node node = Current.Nodes[i];
Button button = Instantiate( Resources.Load<GameObject>( "Prefabs/Button" ) ).GetComponent<Button>();
button.transform.SetParent( content );
button.GetComponentInChildren<Text>().text = node.name;
button.onClick.AddListener( delegate
{
Debug.Log( button.GetComponentInChildren<Text>().text );
} );
}
}
Clicking on buttons always output node1
Just assign to a new local variable on each iteration and use that local variable for the AddListener.
EDIT
Quick example:
MyFunction will always receive 100 as the value of i on this:
for (int i=0; i<100; i++) {
button.onClick.AddListener(() => MyFunction(i));
}
But if you do this it will work correctly:
for (int i=0; i<100; i++) {
int iLocal = i;
button.onClick.AddListener(() => MyFunction(iLocal));
}
Everything because of this code is in couroutine. Here is the answer http://answers.unity3d.com/answers/974195/view.html

How do I tell if a playlist is a music playlist in c#?

I've attached my code so far, my problem is all the playlists return "auto" or "wpl" for their type. (This is all using a WMPLib reference)
mediaplayer = new WindowsMediaPlayer();
// Init. Playlists
IWMPPlaylistCollection plcollection = mediaplayer.playlistCollection;
plarray = plcollection.getAll();
int i = 0, count = plarray.count;
string[] t = new string[count];
// Here is where I want to sort out non-music playlists
// And add them to the ListBox
for (i = 0; i < count - 1; i++)
t[i] = plarray.Item(i).getItemInfo("PlaylistType");
for (i = 0; i < count; i++ )
PlaylistBox.Items.Add("" + plarray.Item(i).name);
Unrelated, but if you know how to attach the playlists as data instead of strings that would be helpful too :)
I guess you can iterate through playlist items and if item's "MediaType" attribute equals "audio" deem this list as the one containing audio files. Smth like this:
private bool ListHasAudio(IWMPPlaylist playList)
{
if (playList != null && playList.count > 0)
{
for (int n = 0; n < playList.count; n++)
{
IWMPMedia media = playList.get_Item(n);
string mediaType = media.getItemInfo("MediaType");
if (mediaType != null && mediaType.Equals("audio", StringComparison.CurrentCultureIgnoreCase))
return true;
}
}
return false;
}
here's how you can use it:
var mediaplayer = new WindowsMediaPlayer();
// Init. Playlists
IWMPPlaylistCollection plcollection = mediaplayer.playlistCollection;
var plarray = plcollection.getAll();
// Load list box items
for (int i = 0; i < plarray.count; i++)
{
IWMPPlaylist playList = plarray.Item(i);
if (ListHasAudio(playList))
PlaylistBox.Items.Add(playList.name);
}
hope this helps, regards

Refactor that code ... Controls.Find method

OK, I have a code (see below):
void M1()
{
for (int i = 1; i < 10; i++)
{
Control[] carr = this.Controls.Find("Number" +
(i - 1).ToString() +
"CheckBox", true);
if ((carr != null) && (carr.Length > 0))
{
CheckBox enableCB = carr[0] as CheckBox;
enableCB.Checked = i % 2 == 0 ? true : false; // or any other value
}
}
}
I don't like iteration with using Controls.Find method. Can I replace it with something easier ?
PS: all the NumberXCheckBox (X>=0 and X<=8) presented on the form.
LINQ enabled, .net 3.5, Visual studio 2008.
Instead of searching by name, can you wrap your CheckBox controls in a container that means you can just iterate through the controls in the container?
I would encourage you to introduce a field in your type to keep references to your checkboxes (an array, a list, a dictionary -- you choose.) This way you'll no longer need to use this non-typed and somewhat ugly find-control-by-key-method.
Anyway, if you're still in .NET 2.0 and prefer to use the Find method, you could simplify a little bit your loop:
for (var i = 0; i <= 8; i++)
{
var controls = Controls.Find("Number" + i + "CheckBox", true);
if (controls.Length > 0)
{
var checkBox = controls[0] as CheckBox;
if (checkBox != null)
checkBox.Checked = i%2 == 0;
}
}
The latest non-nullity test on checkbox can probably be omitted.
If you are using 3.5 or otherwise have LINQ available you could do the following
for ( int i = 0; i < 9; i++) {
var control = this.Controls
.Find(String.Format("Number{0}Checkbox", i))
.Cast<CheckBox>()
.FirstOrDefault();
if ( control != null ) {
control.Checked = (i % 2) != 0;
}
}
More linq-y
for (int i = 0; i < 10; i++) {
var c = (from CheckBox c in this.Controls.Find(String.Format(CultureInfo.InvariantCulture, "Number{0}CheckBox", i-1), true)
select c).FirstOrDefault();
if (c != null) {
c.Checked = i % 2 == 0 ? true : false;
}
}
Here's one without linq, but cleaned up the code a little bit.
for (int i = 1; i < 10; i++)
{
Control[] carr = this.Controls.Find("Number" + (i - 1) + "CheckBox", true);
if (carr.Length <= 0) continue;
CheckBox enableCB = carr[0] as CheckBox;
enableCB.Checked = (i % 2) == 0;
}
[Edit: added the Find(..) code to show why you don't have to check for null]
Here's the frameworks internal code for the Find function. I've added a couple of comments in it
public Control[] Find(string key, bool searchAllChildren)
{
if (string.IsNullOrEmpty(key))
{
throw new ArgumentNullException("key", SR.GetString("FindKeyMayNotBeEmptyOrNull"));
}
// Will always return an ArrayList with zero or more elements
ArrayList list = this.FindInternal(key, searchAllChildren, this, new ArrayList());
// Will always return an Array of zero or more elements
Control[] array = new Control[list.Count];
list.CopyTo(array, 0);
return array;
}

Categories

Resources