I'm assigning values to a class by using ObservableCollection.Class contains MainItems and it's SubItems. Now how can I read all SubItems for each input of MainItem?
public class MainItems
{
public string ItemName { get; set; }
public ObservableCollection<SubItems> SubItemsList { get; set; }
}
public class SubItems
{
public string SubItemName { get; set; }
}
ObservableCollection<MainItems> _data = new ObservableCollection<MainItems>();
for (int i = 1; i <= 5; i++)
{
MainItems _mainItems = new MainItems();
_mainItems.ItemName = "Main" + i.ToString();
_mainItems.SubItemsList = new ObservableCollection<SubItems>();
for (int j = 1; j <= 3; j++)
{
SubItems _subItems = new SubItems()
{
SubItemName = "SubItem" + i.ToString()
};
_mainItems.SubItemsList.Add(_subItems);
}
_data.Add(_mainItems);
}
The foreach loop alway honors the collections(List, Array, Dictionary(special), ...) boundaries and iterates over all Elements, so its the shortest way to achieve what you want. It disallows you to add/remove elements from the currently iterated collection. In this case the classic for loop is your friend.
Full description from Microsoft:
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/foreach-in
Based on Fangs comment:
// foreach Version
foreach (MainItems mainItem in _data)
{
foreach (SubItems subItems in mainItem.SubItemsList)
{
Console.WriteLine($"{mainItem.ItemName} has a child {subItems.SubItemName}!");
}
}
// for Version
for (int i = 0; i < _data.Count; i++)
{
MainItems mainItem = _data[i];
for (int k = 0; k < mainItem.SubItemsList.Count; k++)
{
SubItems subItem = mainItem.SubItemsList[k];
Console.WriteLine($"{mainItem.ItemName} has a child {subItem.SubItemName}!");
}
}
// For Enumerator version
// get the input main item
string input = "Main1";
IEnumerable<ObservableCollection<SubItems>> ItemsforSelectedMainIem = _data.Where(x => x.ItemName == input).Select(x => x.SubItemsList);
var e = ItemsforSelectedMainIem.GetEnumerator();
while (e.MoveNext())
{
var v = e.Current.Select(x=>x.SubItemName).ToList();
foreach (var item in v)
{
Console.WriteLine(item);
}
}
Related
I have diffrent entries in a ListView. I would like to count the entries (starting from the second column). The output should be under the "Total" column (see figure below, framed in red) How can this be achieved? With this code I can only access the individual rows:
for (int j = 1; j < listView1.Items[1].SubItems.Count; j++)
{
string s = listView1.Items[1].SubItems[j].Text;
}
Many thanks for your help!
Here's your solution
//iterate through all rows
for (int i = 0; i < listView1.Items.Count; i++)
{
//make sure that 5 subitems are present
int missingSubitemsCount = 5 - listView1.Items[i].SubItems.Count;
for (int j = 0; j < missingSubitemsCount; j++)
listView1.Items[i].SubItems.Add(new ListViewItem.ListViewSubItem());
int count = 0;
//test columns 1-3
for (int j = 1; j < listView1.Items[i].SubItems.Count - 1; j++)
{
//check if empty
if (String.IsNullOrEmpty(listView1.Items[i].SubItems[j].Text) == false)
count++;
}
//update last column
listView1.Items[i].SubItems[4].Text = count.ToString();
}
However, i think it would be better to use DataGridView and data binding instead. Operating directly on control can cause problems when project gets bigger. Its just better to separate data from view
Here's how i would do it with DataGridView
class MyItem
{
public string Name { get; set; }
public string Property1 { get; set; }
public string Property2 { get; set; }
public string Property3 { get; set; }
public int Total { get; set; }
}
private void button2_Click(object sender, EventArgs e)
{
var items = new List<MyItem>();
items.Add(new MyItem
{
Name = "Books",
Property1 = "A",
Property2 = "B",
Property3 = "C"
});
items.Add(new MyItem
{
Name = "Pencils",
Property1 = "A",
Property2 = "B",
});
items.Add(new MyItem
{
Name = "Tapes",
Property1 = "A",
});
//iterate through all items
foreach (var item in items)
{
//get list of MyItem properties starting with "Property"
foreach (var property in typeof(MyItem).GetProperties().Where(u => u.Name.StartsWith("Property")))
{
//test if null or empty, increment Total if not
if (property.GetValue(item) != null && String.IsNullOrEmpty(property.GetValue(item).ToString()) == false)
item.Total++;
}
//Alternative: Same calculation in one line
item.Total = typeof(MyItem).GetProperties().Count(u => u.Name.StartsWith("Property") && u.GetValue(item) != null && String.IsNullOrEmpty(u.GetValue(item).ToString()) == false);
}
//bind items to DataGridView
dataGridView1.DataSource = items;
}
The problem I'm having is that I'm not sure how to translate my double array into a Dictionary. I have some LINQ Where statements that are pretty heavy, so I wanted to use the keys of my Dictionary to look up a specific value. I have two classes with getters and setters and then my calculator.
I've fiddled around a bit with trying to make either a lookup or a Dictionary, but didn't have any luck.
public class Product
{
public int EarliestOriginYear { get; set; }
public int NoOfDevelopmentYears { get; set; }
public string ProductName { get; set; }
public IEnumerable<ProductIncrementalValue> ProductIncrementalValues { get; set; }
}
public class ProductIncrementalValue
{
public string ProductName { get; set; }
public int OriginYear { get; set; }
public int DevelopmentYear { get; set; }
public double IncrementalValue { get; set; }
}
public IList<double> Calculate(Product product)
{
IList<double> cumulativeDataTriangle = new List<double>();
if (!product.ProductIncrementalValues.Any())
return cumulativeDataTriangle;
for (int i = 0; i < product.NoOfDevelopmentYears; i++)
{
// This is what I want to change (where statements)
var incrementalValues = product.ProductIncrementalValues
.Where(v => v.OriginYear == product.EarliestOriginYear + i)
.ToList();
double previous = 0;
for (int j = 0; j < product.NoOfDevelopmentYears - i; j++)
{
// This is what I want to change
double incrementalValue = incrementalValues.Where(val =>
val.DevelopmentYear == val.OriginYear + j)
.Select(x => x.IncrementalValue)
.FirstOrDefault();
var tmp = incrementalValue + previous;
cumulativeDataTriangle.Add(tmp);
previous = tmp;
}
}
return cumulativeDataTriangle;
}
You could group the products by OriginYear and DevelopmentYear before the loops. Something like this might help:
public IList<double> Calculate(Product product)
{
IList<double> cumulativeDataTriangle = new List<double>();
if (!product.ProductIncrementalValues.Any())
return cumulativeDataTriangle;
var lookup = product.ProductIncrementalValues.ToLookup(v => (v.OriginYear, v.DevelopmentYear), v => v.IncrementalValue);
for (int i = 0; i < product.NoOfDevelopmentYears; i++)
{
var originYear = product.EarliestOriginYear + i;
double previous = 0;
for (int j = 0; j < product.NoOfDevelopmentYears - i; j++)
{
var developmentYear = originYear + j;
var incrementalValues = lookup[(originYear, developmentYear)];
double incrementalValue = incrementalValues.FirstOrDefault();
var tmp = incrementalValue + previous;
cumulativeDataTriangle.Add(tmp);
previous = tmp;
}
}
return cumulativeDataTriangle;
}
Thanks to the previous answer, I managed to implement a Dictionary based on OriginYear and DevelopmentYear, which returns an IncrementalValue.
public IList<double> Calculate(Product product)
{
IList<double> cumulativeDataTriangle = new List<double>();
if (!product.ProductIncrementalValues.Any())
return cumulativeDataTriangle;
var lookup = product.ProductIncrementalValues.
ToDictionary(v => (v.OriginYear, v.DevelopmentYear), v => v.IncrementalValue);
for (int i = 0; i < product.NoOfDevelopmentYears; i++)
{
var originYear = product.EarliestOriginYear + i;
double previous = 0;
for (int j = 0; j < product.NoOfDevelopmentYears - i; j++)
{
var developmentYear = originYear + j;
double incrementalValue;
lookup.TryGetValue((originYear, developmentYear), out incrementalValue);
var tmp = incrementalValue + previous;
cumulativeDataTriangle.Add(tmp);
previous = tmp;
}
}
return cumulativeDataTriangle;
}
When I use if (moscowCars.Contains(cars[x].Name)) it founds the value in a collection and I believe that not necessary to use moscowCars.RemoveAt(moscowCars.FindIndex(o => o.Equals(cars[x].Name))); to find it for a second time, just: moscowCars.Remove(cars[x].Name);. Of course, I can use try & catch instead of if, but I just want to know can I get the item index using Contains?
using System.Collections.Generic;
namespace Autoworld
{
class GoodCars
{
static List<Tech> cars = new List<Tech>();
public class Tech
{
public string Name { get; set; }
public double KM { get; set; }
}
static void Main()
{
List<string> moscowCars = new List<string>
{
"GAZ-330811 Aper", "Lada Vesta Sport"
};
cars.Add(new Tech() { Name = "Lada Vesta Sport", KM = 190 });
for (int x = 0; x < cars.Count; x++)
{
if (moscowCars.Contains(cars[x].Name))
{
moscowCars.RemoveAt(moscowCars.FindIndex(o => o.Equals(cars[x].Name)));
}
}
}
}
}
You could remove the two-step process entirely and just use .Remove which will return:
true if item is successfully removed; otherwise, false. This method
also returns false if itemwas not found in the List.
This would then look like:
for (int x = 0; x < cars.Count; x++)
{
moscowCars.Remove(cars[x].Name);
}
And if you need to handle the case where no car is found to be removed, you can wrap that call in an if condition like:
for (int x = 0; x < cars.Count; x++)
{
if (!moscowCars.Remove(cars[x].Name))
{
// Handle no cars to remove
}
}
Worth noting that behind the scenes, .Remove ultimately just gets the index and then removes the item at that index (which is what you were originally trying to do anyways):
public bool Remove(T item) {
int index = IndexOf(item);
if (index >= 0) {
RemoveAt(index);
return true;
}
return false;
}
See here for the source.
Alternatively, as others have stated, if you expect the List to contain more than item to be removed, you can use .RemoveAll:
moscowCars.RemoveAll(y => y == cars[x].Name);
And again, to handle the case where nothing is found:
if (moscowCars.RemoveAll(y => y == cars[x].Name) == 0)
{
// Handle no cars to remove
}
You can indeed use IndexOf(item)
this will give you the index of the item, or -1 if 'item' was not found (making this method double as a "contains" as well)
Use simply RemoveAt if you are sure you don't have any duplicated items anyway use the second way.
Solution
static List<Tech> cars = new List<Tech>();
public class Tech
{
public string Name { get; set; }
public double KM { get; set; }
}
static void Main()
{
List<string> moscowCars = new List<string>
{
"GAZ-330811 Aper", "Lada Vesta Sport"
};
cars.Add(new Tech() { Name = "Lada Vesta Sport", KM = 190 });
for (int x = 0; x < cars.Count; x++)
{
if (moscowCars.Contains(cars[x].Name))
{
moscowCars.RemoveAt(moscowCars.IndexOf(cars[x].Name));
}
}
}
Solution
static List<Tech> cars = new List<Tech>();
public class Tech
{
public string Name { get; set; }
public double KM { get; set; }
}
static void Main()
{
List<string> moscowCars = new List<string>
{
"GAZ-330811 Aper", "Lada Vesta Sport"
};
cars.Add(new Tech() { Name = "Lada Vesta Sport", KM = 190 });
for (int x = 0; x < cars.Count; x++)
{
if (moscowCars.Contains(cars[x].Name))
{
moscowCars.RemoveAll(o => o == cars[x].Name);
}
}
}
I hope it will help.
I have created an object with the attributes String, and the other is a List<String>.
I have also created a static List<MyObject> where i add then all my Objects.
Now my Problem is the second attribute is getting overridden.
For example I have 3 Objects:
Object1: "Name"; List with 3 Strings
Object2: "Name2"; List with 2 Strings
Object3: "Name3"; List with 5 Strings
If i add them now to my Object List, it looks like so
Name; List with 5 Strings
Name2; List with 5 Strings
Name3; List with 5 Strings
It override the second attributes to all the other Objects in the List.
Code:
for (int i = 0; i < 100; i++)
{
if (elo.ReadObjMask(i) > 0)
{
var iRet = elo.PrepareObjectEx(0, 0, i);
maskenname = elo.ObjMName();
if (maskenname != "")
{
for (int e = 0; e < 50; e++)
{
string eigenschaft = elo.GetObjAttribName(e);
if (eigenschaft != "" && eigenschaft != "-")
{
eigenschaften.Add(eigenschaft);
}
}
allMasks.Add(maskenname);
}
else
{
// Do nothing
}
EloMask emask = new EloMask(maskenname, eigenschaften);
staticVariables.allMask.Add(emask);
eigenschaften.Clear();
}
}
Here is my object class:
public class EloMask
{
public string name;
public List<String> eigenschaften;
public EloMask(string iname, List<String> ieigenschaften)
{
name = iname;
eigenschaften = ieigenschaften;
}
}
The List<string> always points to the same instance because you are passing a reference to the list, not a copy. As a result, the list is cleared and filled again for each EloMask that you pass that list into.
To fix your issue, create a new list instead:
if (elo.ReadObjMask(i) > 0)
{
var iRet = elo.PrepareObjectEx(0, 0, i);
maskenname = elo.ObjMName();
// create a new list here!!!
var eigenschaften = new List<string>();
if (maskenname != "")
{
for (int e = 0; e < 50; e++)
{
string eigenschaft = elo.GetObjAttribName(e);
if (eigenschaft != "" && eigenschaft != "-")
{
eigenschaften.Add(eigenschaft);
}
}
allMasks.Add(maskenname);
}
EloMask emask = new EloMask(maskenname, eigenschaften);
staticVariables.allMask.Add(emask);
// clearing the list is no longer needed
}
Here is an example how you can do what you want:
public static List<Person> PersonsList = new List<Person>();
public static Random rd = new Random();
static void Main(string[] args)
{
for (int i = 0; i < 5; i++)
{
List<string> tmpAbilities = new List<string>() {((char)rd.Next(255)).ToString(), ((char)rd.Next(255)).ToString() , ((char)rd.Next(255)).ToString() };
Person tmpPerson = new Person("TmpName_"+i,tmpAbilities);
PersonsList.Add(tmpPerson);
}
foreach (var persona in PersonsList)
{
Console.WriteLine(persona);
}
}
public class Person
{
public string Name { get; set; }
public List<string> Abilities;
public Person(string name,List<string> abilities)
{
Name = name;
Abilities = abilities;
}
public override string ToString()
{
string retVal = $"Name: {Name}\n";
foreach (var ability in Abilities)
{
retVal += $"Ability : {ability}\n";
}
return retVal;
}
}
for (int i = 0; i < 100; i++)
{
if (elo.ReadObjMask(i) > 0)
{
// Create a new listin here
eigenschaften = new List<string>();
var iRet = elo.PrepareObjectEx(0, 0, i);
maskenname = elo.ObjMName();
if (maskenname != "")
{
for (int e = 0; e < 50; e++)
{
string eigenschaft = elo.GetObjAttribName(e);
if (eigenschaft != "" && eigenschaft != "-")
{
eigenschaften.Add(eigenschaft);
}
}
allMasks.Add(maskenname);
}
else
{
// Do nothing
}
EloMask emask = new EloMask(maskenname, eigenschaften);
staticVariables.allMask.Add(emask);
}
}
I am trying to remove information that is in the third level of a dictionary and can only use it after the removal of this information.
But I can not, what am I doing wrong?
public class Person
{
int id;
string name;
public string Name
{
get { return name; }
set { name = value; }
}
public int ID
{
get { return id; }
set { id = value; }
}
public List<Product> ListProd;
}
public class Product
{
public int idProd;
public string Description;
public List<Tax> listTax;
}
public class Tax
{
public int idTax;
public string Value;
}
//Method
public void SomeMethod()
{
Dictionary<int, List<int>> dicRemove = new Dictionary<int, List<int>>();
List<int> listTaxToRemove = new List<int>();
for (int m = 8; m < 10; m++)
{
listTaxToRemove.Add(m);
}
dicRemove.Add(10, listTaxToRemove);
Dictionary<int, List<Person>> dic = new Dictionary<int, List<Person>>();
List<Person> list = new List<Person>();
for (int i = 0; i < 2; i++)
{
Person d = new Person();
d.ID = i;
d.Name = "Person " + i;
d.ListProd = new List<Product>();
for (int j = 3; j < 6; j++)
{
Product p = new Product();
p.idProd = j;
p.Description = "Product " + j;
d.ListProd.Add(p);
p.listTax = new List<Tax>();
for (int m = 7; m < 10; m++)
{
Tax t = new Tax();
t.idTax = m;
t.Value = "Tax " + m;
p.listTax.Add(t);
}
}
list.Add(d);
}
dic.Add(10, list);
var q = dic.Select(s => s.Value
.Select(s1 => s1.ListProd
.Select(s2 => s2.listTax
.RemoveAll(r =>!dicRemove[s.Key].Contains(r.idTax))))).ToList();
}
I tried a number of ways, through iterations, this approach had just deleting unnecessary records.
Thank you!
RemoveAll is not invoked due to deferred execution of Select
you have 3 Select and only one ToList()
q has type System.Collections.Generic.List'1[System.Collections.Generic.IEnumerable'1[System.Collections.Generic.IEnumerable'1[System.Int32]]]
there are nested IEnumerable object which were not enumerated
here is a working variant but absolutely unreadable, don't do it like this
var q = dic.Select(s => s.Value.Select(s1 => s1.ListProd.Select(s2 => s2.listTax.RemoveAll(r=>!dicRemove[s.Key].Contains(r.idTax)))
.ToList())
.ToList())
.ToList();
improved variant
foreach(var pair in dic)
foreach(var person in pair.Value)
foreach(var prod in person.ListProd)
prod.listTax.RemoveAll(t=>!dicRemove[pair.Key].Contains(t.idTax));