how to assign linq result to multidimensional array - c#

i want to assign the linq result to a multidimensional array and the below is my code, can any one tell me what i am doing wrong.
var query = (from b in db.FourBodyImages
where b.Status == true
orderby b.CreaedDate descending
select new { b.BodyImage, b.Description, b.HeaderName }
).Take(4).ToArray();
if(query.Count()>0)
{
string[,,] x = new string[4, 4,4];
x[,,] = query;
for (int i = 0; i < query.Length; i++)
{
if (i == 0)
{
imgBody1.ImageUrl = "FourBodyImages/" + x[0,0,0].ToString();
}
if (i == 1)
{
imgBody2.ImageUrl = "FourBodyImages/" + x[1,1,1].ToString();
}
if (i == 2)
{
imgBody3.ImageUrl = "FourBodyImages/" + x[2,2,2].ToString();
}
if (i == 3)
{
imgBody4.ImageUrl = "FourBodyImages/" + x[3,3,3].ToString();
}
}
}

If you just need to get 4 objects from the database, and then use them to fill 4 images, all you need to do is:
//this generates an array of (up to 4) objects of an anonymous type
var query = (from b in db.FourBodyImages
where b.Status == true
orderby b.CreaedDate descending
select new { b.BodyImage, b.Description, b.HeaderName }
).Take(4).ToArray();
//no need for count, we can use the array's length
if (query.Length < 4)
{
//i have no idea about your requirements,
//but if we have less than 4 images - panic
throw new InvalidOperationException()
}
//no need for the "for-if", that's an anti-patern
imgBody1.ImageUrl = "FourBodyImages/" + query[0].BodyImage;
imgBody2.ImageUrl = "FourBodyImages/" + query[1].BodyImage;
imgBody3.ImageUrl = "FourBodyImages/" + query[2].BodyImage;
imgBody4.ImageUrl = "FourBodyImages/" + query[3].BodyImage;

It's not clear why you want a three-dimensional array to start with. You're only taking 4 values... why is that not just a one-dimensional array?
var query = (from b in db.FourBodyImages
where b.Status == true
orderby b.CreaedDate descending
select new { b.BodyImage, b.Description, b.HeaderName }
).Take(4).ToArray();
var bodies = new[] { imgBody1, imgBody2, imgBody3, imgBody4 };
for (int i = 0; i < query.Length; i++)
{
// Or whichever property you're interested in...
bodies[i].ImageUrl = "FourBodyImages/" + query[i].BodyImage;
}
If this doesn't help, please give us more idea of what you're actually trying to achieve. Why does your query return three properties, for example, and how do you want to use them?

Related

C# LINQ - SkipWhile() in reverse, without calling Reverse()?

In this code:
for (e = 0; e <= collection.Count - 2; e++)
{
var itm = collection.Read()
var itm_price = itm.Price
var forwards_satisfied_row = collection
.Skip(e + 1)
.SkipWhile(x => x.Price < ex_price)
.FirstOrDefault();
var backwards_satisfied_row = collection
.Reverse()
.Skip(collection.Count - e)
.SkipWhile(x => x.Price < ex_price)
.FirstOrDefault();
}
Suppose the collection contains millions of items and a Reverse() is too expensive, what would be the best way to achieve the same outcome as 'backwards_satisfied_row' ?
Edit:
For each item in the collection, it should find the first preceding item that matches the SkipWhile predicate.
For context I'm finding the distance a price extrema (minima or maxima) is from a horizontal clash with the price. This gives a 'strength' value for each Minima and Maxima, which determines the importance of it, and to help marry it up with extremas of a similar strength.
Edit 2
This chart shows the data in the reproc code below, note the dip in the middle at item #22, this item has a distance of 18.
Bear in mind this operation will be iterated millions of times.
So I'm trying not to read into memory, and to only evaluate the items needed.
When I run this on a large dataset r_ex takes 5 ms per row, whereas l_ex takes up to a second.
It might be tempting to iterate backwards and check that way, but there could be millions of previous records, being read from a binary file.
Many types of searches like Binary search wouldn't be practical here, since the values aren't ordered.
static void Main(string[] args)
{
var dict_dists = new Dictionary<Int32, Int32>();
var dict = new Dictionary<Int32, decimal> {
{1, 410},{2, 474},{3, 431},
{4, 503},{5, 461},{6, 535},
{7, 488},{8, 562},{9, 508},
{10, 582},{11, 522},{12, 593},
{13, 529},{14, 597},{15, 529},
{16, 593},{17, 522},{18, 582},
{19, 510},{20, 565},{21, 492},
{22, 544},{23, 483},{24, 557},
{25, 506},{26, 580},{27, 524},
{28, 598},{29, 537},{30, 609},
{31, 543},{32, 612},{33, 542},
{34, 607},{35, 534},{36, 594},
{37, 518},{38, 572},{39, 496},
{40, 544},{41, 469},{42, 511},
{43, 437},{44, 474},{45, 404},
{46, 462},{47, 427},{48, 485},
{49, 441},{50, 507}};
var i = 0;
for (i = 0; i <= dict.Count - 2; i++)
{
var ele = dict.ElementAt(i);
var current_time = ele.Key;
var current_price = ele.Value;
var is_maxima = current_price > dict.ElementAt(i + 1).Value;
//' If ele.Key = 23 Then here = True
var shortest_dist = Int32.MaxValue;
var l_ex = new KeyValuePair<int, decimal>();
var r_ex = new KeyValuePair<int, decimal>();
if (is_maxima)
{
l_ex = dict.Reverse().Skip(dict.Count - 1 - i + 1).SkipWhile(x => x.Value < current_price).FirstOrDefault();
r_ex = dict.Skip(i + 1).SkipWhile(x => x.Value < current_price).FirstOrDefault();
}
else
{ // 'Is Minima
l_ex = dict.Reverse().Skip(dict.Count - 1 - i + 1).SkipWhile(x => x.Value > current_price).FirstOrDefault();
r_ex = dict.Skip(i + 1).SkipWhile(x => x.Value > current_price).FirstOrDefault();
}
if (l_ex.Key > 0)
{
var l_dist = (current_time - l_ex.Key);
if ( l_dist < shortest_dist ) {
shortest_dist = l_dist;
};
}
if (r_ex.Key > 0)
{
var r_dist = (r_ex.Key - current_time);
if ( r_dist < shortest_dist ) {
shortest_dist = r_dist;
};
}
dict_dists.Add(current_time, shortest_dist);
}
var dist = dict_dists[23];
}
Edit: As a workaround I'm writing a reversed temp file for the left-seekers.
for (i = file.count - 1; i >= 0; i += -1)
{
file.SetPointerToItem(i);
temp_file.Write(file.Read());
}
You could make it more efficient by selecting the precedent of each item in one pass. Lets make an extension method for enumerables that selects a precedent for each element:
public static IEnumerable<T> SelectPrecedent<T>(this IEnumerable<T> source,
Func<T, bool> selector)
{
T selectedPrecedent = default;
foreach (var item in source)
{
if (selector(item)) selectedPrecedent = item;
yield return selectedPrecedent;
}
}
You could then use this method, and select the precedent and the subsequent of each element by doing only two Reverse operations in total:
var precedentArray = collection.SelectPrecedent(x => x.Price < ex_price).ToArray();
var subsequentArray = collection.Reverse()
.SelectPrecedent(x => x.Price < ex_price).Reverse().ToArray();
for (int i = 0; i < collection.Count; i++)
{
var current = collection[i];
var precedent = precedentArray[i];
var subsequent = subsequentArray[i];
// Do something with the current, precedent and subsequent
}
No need to do .Reverse() and then FirstOrDefault(), just use LastOrDefault(). Instead of Skip(collection.Count - e) use .Take(e) elements
var backwards_satisfied_row = collection
.SkipWhile(x => x.Price < ex_price) //Skip till x.Price < ex_price
.Skip(e+1) //Skip first e+1 elements
.LastOrDefault(); //Get Last or default value
You can make your code more efficient by storing collection and then just get FirstOrDefault() and LastOrDefault() for forwards_satisfied_row and backwards_satisfied_row respectively.
like,
for (e = 0; e <= collection.Count - 2; e++)
{
var itm = collection.Read()
var itm_price = itm.Price
var satisfied_rows = collection
.SkipWhile(x => x.Price < ex_price)
.Skip(e + 1)
.ToList();
var forwards_satisfied_row = satisfied_rows.FirstOrDefault();
var backwards_satisfied_row = satisfied_rows.LastOrDefault();
}

Adding to List<string> but having trouble with the items getting added

I have 2 lists with 200 items in each.
List A contains the names of the products.
List B contains the prices of the products.
I have another list (List C) with 250 items inside which include names and prices of products.
Now, I am trying to compare the names of List A to see if they exist in List C and if they do I want to use the price in List C.
This is what I've tried so far:
foreach (var item in wiznzList)
{
for (int j = 0; j < nameList.Count; j++)
{
if (nameList[j] == item.ProductName)
{
wizNzPriceList.Add(item.Price);
}
else
{
wizNzPriceList.Add("No Product");
}
}
}
I want to go through the list of names and check if they exist in List C if they do I want to add the price from List C and if it doesn't exist I want it to write No Product.
So, in the end I want 3 lists with 200 items in each.
Instead, when I run this I get 10040000 items added into the list. Could someone please point me in the right direction.
If i understand your problem correctly (and the jury is out on that)
You could use ToDictionary and Select
var dict = products.ToDictionary(x => x.ProductName, x => x.Price);
var results = names.Select(x => dict.TryGetValue(x, out var value) ? value.ToString() : "No Product");
Note : You could use Any for O(n^2) time complexity. However, ToDictionary would be more efficient
I have found a code that does what you want it to do !
Object Product(string pName, float pPrice).
listA list of Products so you can store and change price.
listB list of float, not used.
list C list of Products with pName and pPrice.
The CompareA_With_C() method first checks for same products and if true put price of list C in listA and breaks out of inner loop.
The else if statement waits till the loop has gone through the whole of listC and if no pName match found, changes pName to "No Product" in listA.
Code :
void CompareA_With_C()
{
for (int i = 0; i < listA.Count; i++)
{
for (int j = 0; j < listC.Count; j++)
{
string a = listA[i].pName;
string c = listC[j].pName;
if (string.Equals(a, c))
{
listA[i].pPrice = listC[j].pPrice;
break;
}
else if (j == (listC.Count - 1))
{
listA[i].pName = "No Product";
listB[i] = 0.0f;
}
}
}
}
You can try to use LINQ and replace your inner loop with Any method.
foreach (var item in wiznzList)
{
if (nameList.Any(p => p == item.ProductName)
{
wizNzPriceList.Add(item.Price);
}
else
{
wizNzPriceList.Add("No Product");
}
}
I was able to solve my problem by using the code below, Thank you very much Crispi.
for (int i = 0; i < nameList.Count; i++)
{
for (int j = 0; j < wiznzList.Count; j++)
{
string a = nameList[i];
string c = wiznzList[j].ProductName;
if (string.Equals(a, c))
{
wizNzPriceList.Add(wiznzList[j].Price);
break;
}
else if (j == (wiznzList.Count - 1))
{
wizNzPriceList.Add("No Product");
}
}
}
I now get 200 results as required. Thank you everyone for your time.

access an array element inside a LINQ to Entities set

I am using a Split method on a string inside a Linq query but only need the second element. I get a "System.InvalidOperationException: 'Unrecognized expression node: ArrayIndex'" on the below code:
var RMA_stops_all = (from rma in rDb.DistributionStopInformations
join line in rDb.DistributionLineItems on rma.UniqueIdNo equals line.UniqueIdNo
where line.RmaNumber != null
&&
(line.DatetimeCreated > Convert.ToDateTime(dateToCheck_rma) &&
line.DatetimeCreated < Convert.ToDateTime(dateToCheck_rma).AddDays(7))
&& rma.CustomerNo == TNGCustNo
select new
{
dtCreated = line.DatetimeCreated,
UniqueIdNo = rma.UniqueIdNo,
RmaNumber = line.RmaNumber,
RmaOriginalUniqueId = line.RmaOriginalUniqueId,
ItemSequenceNo = line.ItemSequenceNo,
ItemNumber = line.ItemNumber,
goodRMA_flag = line.RmaNumber.Contains("/078"),
rmaGood = line.RmaNumber.Split(new string[] { "/" }, StringSplitOptions.None)[1]
}).ToArray();
if I remove the [1] on the array it works and I can access both elements fine for the entire dataset with:
foreach (var item in RMA_stops_all)
{
var right = RMA_stops_all.First().rmaGood[1];
var left = RMA_stops_all.First().rmaGood[0];
Console.WriteLine("left {0} - right{1} ",left.ToString(), right.ToString());
}
EDIT - duh. The above "test" was completely useless (as gently pointed out) - however, the below does prove out that it is working correctly (some of the returned values only have 1 element, hence the additional if block - the output is as expected:
foreach (var item in RMA_stops_all)
{
string right, left;
if (item.rmaGood.Length == 1)
{
left = item.rmaGood[0].ToString();
right = "Not there";
}
else
{
left = item.rmaGood[0].ToString();
right = item.rmaGood[1].ToString();
}
Console.WriteLine("left {0} - right{1} ", left, right);
}
SQL Output from dbMonitor:
SELECT t2.datetime_created AS "DatetimeCreated",
t1.unique_id_no AS "UniqueIdNo",
t2.rma_number AS "RmaNumber",
t2.rma_original_unique_id AS "RmaOriginalUniqueId",
t2.item_sequence_no AS "ItemSequenceNo",
t2.item_number AS "ItemNumber",
(t2.rma_number LIKE :p3) OR (t2.rma_number LIKE :p4) AS "C1",
t2.rma_number AS "RmaNumber1"
FROM cops_reporting.distribution_stop_information t1
INNER JOIN cops_reporting.distribution_line_items t2
ON t1.unique_id_no = t2.unique_id_no
WHERE (t2.rma_number IS NOT NULL)
AND (t2.datetime_created > :p0)
AND (t2.datetime_created < :p1)
AND (t1.customer_no = :p2)
I didn't realize Linq to Entities had access to some SQL functions directly, so this may work for you:
var RMA_stops_all = (from rma in rDb.DistributionStopInformations
join line in rDb.DistributionLineItems on rma.UniqueIdNo equals line.UniqueIdNo
where line.RmaNumber != null
&&
(line.DatetimeCreated > Convert.ToDateTime(dateToCheck_rma) &&
line.DatetimeCreated < Convert.ToDateTime(dateToCheck_rma).AddDays(7))
&& rma.CustomerNo == TNGCustNo
select new
{
dtCreated = line.DatetimeCreated,
UniqueIdNo = rma.UniqueIdNo,
RmaNumber = line.RmaNumber,
RmaOriginalUniqueId = line.RmaOriginalUniqueId,
ItemSequenceNo = line.ItemSequenceNo,
ItemNumber = line.ItemNumber,
goodRMA_flag = line.RmaNumber.Contains("/078"),
rmaGood = line.RmaNumber.Substring(line.RmaNumber.IndexOf("/")+1)
}).ToArray();
assuming that RmaNumber looks like #/# and doesn't have additional slashes you want to avoid.
I don't see why it would work without the index operation either, and your SQL doesn't seem to reflect the same Linq code (have you used LinqPad?), but I would suggest since you are doing .ToArray() anyway, moving the split/index to the client:
var RMA_stops_all = (from rma in rDb.DistributionStopInformations
join line in rDb.DistributionLineItems on rma.UniqueIdNo equals line.UniqueIdNo
where line.RmaNumber != null
&&
(line.DatetimeCreated > Convert.ToDateTime(dateToCheck_rma) &&
line.DatetimeCreated < Convert.ToDateTime(dateToCheck_rma).AddDays(7))
&& rma.CustomerNo == TNGCustNo
select new
{
dtCreated = line.DatetimeCreated,
rma.UniqueIdNo,
line.RmaNumber,
line.RmaOriginalUniqueId,
line.ItemSequenceNo,
line.ItemNumber,
goodRMA_flag = line.RmaNumber.Contains("/078"),
}).AsEnumerable().Select(r => new {
r.dtCreated,
r.UniqueIdNo,
r.RmaNumber,
r.RmaOriginalUniqueId,
r.ItemSequenceNo,
r.ItemNumber,
r.goodRMA_flag,
rmaGood = r.RmaNumber.Split(new string[] { "/" }, StringSplitOptions.None)[1]
});
BTW, you don't have to repeat field/property names for a simple accessor expression so I removed them from your original select. Also, if you can live with the double indirection, you could just include r in the second select.

Is it possible to create a best fit Linq query from a list of classes?

Hi I have a function which returns the best fit of a class from a collection. I know how to replace the foreach statement using a linq query to get a new list but I would like to know if it is possible to returna single result using a linq statement that would equate to the best fit similar to the code below.
private ProductDefaults GetBestFitProductDefault(Product product,IList<ProductDefaults> defaultList, ProductDefaultsTypeEnum type)
{
ProductDefaults pdef = null;
var matches = -1;
var bestfit = matches;
foreach (var def in defaultList)
{
if(def.DefaultType == type)
{
matches = 0;
if (def.Level1Ref == product.Level1Ref)
{
matches++;
}
if (def.Level2Ref == product.Level2Ref)
{
matches++;
}
if (def.Level3Ref == product.Level3Ref)
{
matches++;
}
if (def.Level4Ref == product.Level4Ref)
{
matches++;
}
if (def.Level5Ref == product.Level5Ref)
{
matches++;
}
if (def.Level6Ref == product.Level6Ref)
{
matches++;
}
if(matches > bestfit)
{
bestfit = matches;
pdef = def;
}
}
}
return pdef;
}
After a bit of research I have come up with this Linq query to replace the code hopefully this will help someone else who may need a similar solution. The let allows me to create a new column that will calculate the bestfit or number of matches. By ordering the result descending by the bestfit I am certain that the first object will have the highest bestfit value so is the one I need. There could potentially be multiple products with the same bestfit score in this case it would be random which one was chosen but in my case this would be acceptable.
private ProductDefaults GetBestFitProductDefault(Product product, IList<ProductDefaults> defaultList, ProductDefaultsTypeEnum type)
{
ProductDefaults pdef = null;
var list =
from pd in defaultList
let bestfit = pd.Level1Ref == product.Level1Ref ? 1 : 0 +
pd.Level2Ref == product.Level2Ref ? 1 : 0 +
pd.Level3Ref == product.Level3Ref ? 1 : 0 +
pd.Level4Ref == product.Level4Ref ? 1 : 0 +
pd.Level5Ref == product.Level5Ref ? 1 : 0 +
pd.Level6Ref == product.Level6Ref ? 1 : 0
where pd.DefaultType == type
orderby bestfit descending
group pd by bestfit into pdl
select new { pdl.Key, best = pdl.ToList() };
pdef = list.First().best.FirstOrDefault();
return pdef;
}

c# linq join on a sublist in a list

I have a list: var_assets_root that has a nested sub list: contents. I can do a join on the root list and augment it with extra items from the two other lists lst_invtypes_assets and lst_stations_composite_retribution by doing two joins on index items in var_assets_root. But I also want to do a join on the items that are within the sublist var_assets_root-->contents\b.contents which the code below is not doing so far.
Image>>structure of var_assets_root in watch window
mysql mysql_object = new mysql();
List<mysql.invtypes_asset> lst_invtypes_assets = mysql_object.dbmysql_invtypes_asset_to_list();
List<mysql.stations_composite_retribution> lst_stations_composite_retribution = mysql_object.dbmysql_select_stastations_composite_retribution_to_list();
var lst_assets_list = new AssetList("134399", "343434SxSFX7qO81LqUCberhS1OQtktMvARFGED0ZRSN5c4XP230SA", "434367527");
lst_assets_list.Query();
var var_assets_root = lst_assets_list.assets.ToList();
var var_assets_root_typeid_station
= from b in var_assets_root
join c in lst_invtypes_assets on b.typeID equals c.typeID
join d in lst_stations_composite_retribution on b.locationID equals d.stationID
select new {b.contents, b.flag, b.itemID, b.locationID, d.solarSystemName, d.security, d.securityClass, d.regionName, b.quantity, b.singleton, b.typeID, c.groupID, c.marketGroupID, c.volume,
c.typeName, c.description};
I have done a solution of running a linq query for every content sub list and assigning values accordingly.
private void btn_evenet_Click(object sender, EventArgs e)
{
Stopwatch stopwatch1 = new Stopwatch();
Stopwatch stopwatch2 = new Stopwatch();
stopwatch1.Start();
mysql mysql_object = new mysql();
List<mysql.invtypes_asset> lst_invtypes_assets = mysql_object.dbmysql_invtypes_asset_to_list();
List<mysql.stations_composite_retribution> lst_stations_composite_retribution = mysql_object.dbmysql_select_stastations_composite_retribution_to_list();
AssetList lst_assets_list = new AssetList("2312099", "J&VNM14RFUkSxSFX7qAAAAAA1OQtktMvYTVZZBhkO23235c4Z&HJKODPQLM", "123231527");
lst_assets_list.Query();
var var_assets_root = lst_assets_list.assets.ToList();
var var_assets_root_typeid_station
= from b in var_assets_root
join c in lst_invtypes_assets on b.typeID equals c.typeID
join d in lst_stations_composite_retribution on b.locationID equals d.stationID
select new { b.contents, b.flag, b.itemID, b.locationID, d.solarSystemName, d.security, d.securityClass, d.regionName, b.quantity, b.singleton, b.typeID, c.groupID, c.marketGroupID, c.volume,
c.typeName, c.description};
var lst_assets_root_typeid_station = var_assets_root_typeid_station.ToList(); // Using .ToArray() is about 200ms faster than .ToList()
stopwatch2.Start();
for (Int32 a = 0; a < lst_assets_root_typeid_station.Count(); a++)
{
if (lst_assets_root_typeid_station[a].contents.Count() >= 0)
{
for (Int32 b = 0; b < lst_assets_root_typeid_station[a].contents.Count(); b++)
{
var var_row_invtypes_assets = lst_invtypes_assets.Where(x => x.typeID == lst_assets_root_typeid_station[a].contents[b].typeID).ToArray();
lst_assets_root_typeid_station[a].contents[b].groupID = var_row_invtypes_assets[0].groupID;
lst_assets_root_typeid_station[a].contents[b].typeName = var_row_invtypes_assets[0].typeName;
lst_assets_root_typeid_station[a].contents[b].description = var_row_invtypes_assets[0].description;
lst_assets_root_typeid_station[a].contents[b].volume = var_row_invtypes_assets[0].volume;
lst_assets_root_typeid_station[a].contents[b].marketGroupID = var_row_invtypes_assets[0].marketGroupID;
if (lst_assets_root_typeid_station[a].contents[b].contents.Count() != 0)
{
for (Int32 c = 0; c < lst_assets_root_typeid_station[a].contents[b].contents.Count(); c++)
{
var_row_invtypes_assets = lst_invtypes_assets.Where(x => x.typeID == lst_assets_root_typeid_station[a].contents[b].contents[c].typeID).ToArray();
lst_assets_root_typeid_station[a].contents[b].contents[c].groupID = var_row_invtypes_assets[0].groupID;
lst_assets_root_typeid_station[a].contents[b].contents[c].typeName = var_row_invtypes_assets[0].typeName;
lst_assets_root_typeid_station[a].contents[b].contents[c].description = var_row_invtypes_assets[0].description;
lst_assets_root_typeid_station[a].contents[b].contents[c].volume = var_row_invtypes_assets[0].volume;
lst_assets_root_typeid_station[a].contents[b].contents[c].marketGroupID = var_row_invtypes_assets[0].marketGroupID;
}
}
}
}
}
stopwatch2.Stop();
stopwatch1.Stop();
lbl_stopwatch1.Text = "Everything: " + stopwatch1.Elapsed.ToString("mm\\:ss\\.ff"); // 1.53 seconds no debugging
lbl_stopwatch2.Text = "contents sublists: " + stopwatch2.Elapsed.ToString("mm\\:ss\\.ff"); // contents sublists take 1.03 seconds no debugging
}
}
Once I figured that 'yield' pops one content node from a stack of content nodes when getAllContents is called I adapted my code and your function so that now all content nodes get recursed into and have their empty values filled from a linq query. Also the new code that is after the linq creation of var_assets_root_typeid_station is now almost twice as fast. Thank you.
private void btn_evenet_Click(object sender, EventArgs e)
{
Stopwatch stopwatch1 = new Stopwatch();
Stopwatch stopwatch2 = new Stopwatch();
stopwatch1.Start();
mysql mysql_object = new mysql();
List<mysql.invtypes_asset> lst_invtypes_assets = mysql_object.dbmysql_invtypes_asset_to_list();
List<mysql.stations_composite_retribution> lst_stations_composite_retribution = mysql_object.dbmysql_select_stastations_composite_retribution_to_list();
AssetList lst_assets_list = new AssetList("12345678", "ABCDEFFGHIKL01235kokJDSD213123", "12345678");
lst_assets_list.Query();
var var_assets_root = lst_assets_list.assets.ToList();
var var_assets_root_typeid_station
= from b in var_assets_root
join c in lst_invtypes_assets on b.typeID equals c.typeID
join d in lst_stations_composite_retribution on b.locationID equals d.stationID
select new
{b.contents, b.flag, b.itemID, b.locationID, d.solarSystemName, d.security, d.securityClass, d.regionName, b.quantity, b.singleton, b.typeID, c.groupID, c.marketGroupID, c.volume,
c.typeName, c.description};
var lst_assets_root_typeid_station = var_assets_root_typeid_station.ToList(); // Using .ToArray() is about 200ms faster than .ToList()
stopwatch2.Start();
for (Int32 a = 0; a < lst_assets_root_typeid_station.Count(); a++)
{
if (lst_assets_root_typeid_station[a].contents.Count() > 0)
{
for (Int32 z = 0; z < lst_assets_root_typeid_station[a].contents.Count(); z++)
{
foreach (AssetList.Item contentnode in getAllContents(lst_assets_root_typeid_station[a].contents[z]))
{
var var_row_invtypes_assets = lst_invtypes_assets.Where(x => x.typeID == contentnode.typeID).ToArray();
contentnode.groupID = var_row_invtypes_assets[0].groupID;
contentnode.typeName = var_row_invtypes_assets[0].typeName;
contentnode.description = var_row_invtypes_assets[0].description;
contentnode.volume = var_row_invtypes_assets[0].volume;
contentnode.marketGroupID = var_row_invtypes_assets[0].marketGroupID;
}
}
}
}
stopwatch2.Stop();
stopwatch1.Stop();
lbl_stopwatch1.Text = "Everything: " + stopwatch1.Elapsed.ToString("mm\\:ss\\.ff"); // 1.16 seconds no debugging
lbl_stopwatch2.Text = "contents sublists: " + stopwatch2.Elapsed.ToString("mm\\:ss\\.ff"); // contents sublists take 0.63 seconds no debugging
}
IEnumerable<AssetList.Item> getAllContents(AssetList.Item contentNode)
{
if (contentNode.contents.Count == 0)
{
yield return contentNode;
}
else
{
foreach (AssetList.Item subContentNode in contentNode.contents)
{
yield return subContentNode;
foreach (AssetList.Item subSubContentNode in getAllContents(subContentNode))
yield return subSubContentNode;
}
}
}
Aren't you just looking for SelectMany?
var contents = lst_invtypes_assets.contents.SelectMany(x => x.contents);
What I think you're getting at is that you want to access the outer .contents property, and if it contains any contents, then access those, too, and so on... recursively.
While you could do this by defining a recursive Func<AssetList.Item, IEnumerable<AssetList.Item>> and a line of LINQ using .Aggregate, it will probably be easier/more readable to define your own recursive method.
IEnumerable<AssetList.Item> getAllContents(AssetList.Item station) {
foreach (AssetList.Item subStation in station.contents) {
yield return subStation;
foreach (AssetList.Item subSubStation in getAllContents(substation))
yield return subSubStation;
}
}
You can then do a simple foreach(var station in getAllContents(parentStation)), or modify this to include the original station, make it an extension method, etc.

Categories