Values don't leave the stack after Pop (Stack<T>) - c#

The stack is formed from a list that was grouped. Then there are several foreachs where Pop is called.
But the number of values in the stacks does not decrease.
Please explain why. Thank you in advance!
public static void N(){
var test = new List<int>(){1,1,1,2,2,3,3,3,3,4,4,5,6,7,7,7,7,8};
var testGr = test.GroupBy(x=>x);
var stacked = testGr.Select(x=> new {
num = x.Key,
stack = new Stack<int>(x)
});
var testNumOne = stacked.First(x=>x.num==1);
Console.WriteLine(testNumOne.stack.Count());
foreach(var x in new List<int>(){1,2,1,2,3}){
var flush = stacked.First(y => y.num == x).stack.Pop();
}
testNumOne = stacked.First(x=>x.num==1);
Console.WriteLine(testNumOne.stack.Count());
}

The copy of "stacked" solves the problem (through stacked.ToList())
public static void Main()
{
var test = new List<int>(){1,1,1,2,2,3,3,3,3,4,4,5,6,7,7,7,7,8};
var testGr = test.GroupBy(x=>x);
var stacked = testGr.Select(x=> new {
num = x.Key,
stack = new Stack<int>(x)
});
var stackedCopy = stacked.ToList();
var testNumOne = stackedCopy.First(x=>x.num==1);
Console.WriteLine(testNumOne.stack.Count());
foreach(var x in new List<int>(){1,2,1,2,3}){
var flush = stackedCopy.First(y => y.num == x).stack.Pop();
}
testNumOne = stackedCopy.First(x=>x.num==1);
Console.WriteLine(testNumOne.stack.Count());
}
I'm already learning how the Linq library works with memory. Thank you all very much!)

Related

Starting tasks inside another task is duplicating my WebRequests

I use the code below to check some pdf files online and return a string accordingly.
The problem is: When I added the second Task.Factory.StartNew() it started duplicating all requests, but still returning only one answer(as it should be).
I need this to be as fast as possible so I can't waste time sending two requests to the server.
public static void Main(string[] args)
{
var listT = new List<string>()
{
"24006025062"
};
var task = listT.Select(x => Task.Factory.StartNew(() => TesteTask(x)));
Task.WaitAll(task.ToArray(), TimeSpan.FromSeconds(120));
List<string> results = new List<string>();
foreach (var result in task)
{
results.Add(result.Result);
}
}
private static string TesteTask(string codCart)
{
var teste = new Consulta();
var retorno = string.Empty;
var session = teste.GetCaptcha();
for (int i = 0; i < 10; i++)
{
session.CaptchaResolvida = QuebraCaptcha(session.CaptchaCodificada).CaptchaResolvida;
if (session.CaptchaResolvida.Length > 0)
{
var links = teste.Consulta(codCart, session).Retorno;
if (links.Any())
{
var tasks = links.Select(x => Task.Factory.StartNew(() => Executa(teste, session, x)));
Task.WaitAll(tasks.ToArray(), TimeSpan.FromSeconds(120));
var modelList = from Result in tasks select Result.Result;
retorno = teste.FinalizaProcesso(modelList.ToList());
break;
}
}
}
return retorno;
}
private static string Executa(Consulta teste, Model<Request> session, string link)
{
var retorno = string.Empty;
for (int i = 0; i < 10; i++)
{
var CaptchaResolvida = QuebraCaptcha(teste.GetCaptchaPdf(session)).CaptchaResolvida;
if (CaptchaResolvida != null && CaptchaResolvida != string.Empty)
{
var status = teste.BaixaPdf(link, CaptchaResolvida, session);
if (status != string.Empty)
{
retorno = status;
break;
}
}
}
return retorno;
}
Ps: This is my first post on stack overflow, if I'm not clear enough please let me know!
You are getting this behavior because you are iterating twice on the Select returned IEnumerable. Try this:
public static void Main(string[] args)
{
var listT = new List<string>()
{
"24006025062"
};
var task = list
.Select(x => Task.Factory.StartNew(() => TesteTask(x)))
.ToArray();
Task.WaitAll(task, TimeSpan.FromSeconds(120));
List<string> results = new List<string>();
foreach (var result in task)
{
results.Add(result.Result);
}
}
By moving the ToArray() just after the Select() it creates the results IEnumerable only once instead of twice.
Hope it helps!

How do I conditionally update List<Object> items using LINQ

How do I conditionally update List<object> items using LINQ, I have managed to get a way to update all items in List<object> using this syntax:
var updatedList = sourceList.Select( x => { x.PropertyA = NewValue; return x; }).ToList();
var updatedList = sourceList.Select( x => {return x = x.PropertyA == SomeValue ? x1 : x;}).ToList();
You can either project it to a new collection using the .Select, or affect the original list using the .ForEach extension method. Both are shown below, given an arbitrary condition.
class Program
{
static void Main(string[] args)
{
List<testClass> ogList = new List<testClass>();
bool testCondition = true;
//use the select to update and project to new collection
var updated =
ogList
.Select(x =>
{
if (testCondition)
x.testProp = 1;
return x;
}).ToList();
//use the foreach to update original collection
ogList.ForEach(x =>
{
if (testCondition)
x.testProp = 1;
});
}
}
public class testClass
{
public int testProp { get; set; }
}
You can use this code:
sourceList.Where(w => w.CheckProperty == valueofProperty).Select(s => {
s.PropertyA = valueofPropertyA ;
s.PropertyB = valueofPropertyB;
return s;
}).ToList();
Hope this answer will help you.
So here I creating objects of my database, list of my db_table and table.
private DB dbs = new DB();
List<tab1> lst = new List<tab1>();
tab1 tabobj = new tab1();
here you will get the list of data in your table
lst = dbs.tab1.ToList();
````````````
updating the table
```````````
tabobj.c1 = "value";
tabobj.c2 = "value";
adding updated columns to table
dbs.tab1.Add(tabobj);
save changes here
dbs.SaveChanges();
data of updated table
lst = dbs.tab1.ToList();

Pass multiple parameters to a task

I wish to pass two BlockingCollection<>s to a task. I tried to put them in an object array and pass them but it doesn't work. Can anyone help me with this? The code where i am trying to pass the values is written below:
var lineHolders = new[]
{
new BlockingCollection<string>(linesCapacity),
new BlockingCollection<string>(linesCapacity),
new BlockingCollection<string>(linesCapacity),
new BlockingCollection<string>(linesCapacity)
};
var chunksHolder = new[]
{
new BlockingCollection<List<BsonDocument>>(chunksCapacity),
new BlockingCollection<List<BsonDocument>>(chunksCapacity)
};
for (var processors = 0; processors < 16; processors++)
{
var myLineHolder = lineHolders[processors%lineHolders.Length];
var myChunkHolder = chunksHolder[processors%chunksHolder.Length];
processorTaskArray[processors] = Task.Factory.StartNew((arg) =>
{
var lines = (BlockingCollection<string>) arg[0]; // compiler generates error here
var chunks = (BlockingCollection<List<BsonDocument>>) arg[1]; // compiler generates error here
// perform my work...
},
new object []
{
myLineHolder,
myChunkHolder
});
}
You're using the following overload of StartNew:
public Task StartNew(
Action<Object> action,
Object state
)
Since it's just an object you can't apply indexing on it. Cast it and it will work fine.
for (var processors = 0; processors < 16; processors++)
{
var myLineHolder = lineHolders[processors % lineHolders.Length];
var myChunkHolder = chunksHolder[processors % chunksHolder.Length];
processorTaskArray[processors] = Task.Factory.StartNew((arg) =>
{
var properArg = (object[]) arg;
var lines = (BlockingCollection<string>) properArg[0]; // compiler generates error here
var chunks = (BlockingCollection<List<BsonDocument>>) properArg[1]; // compiler generates error here
// perform my work...
},
new object[]
{
myLineHolder,
myChunkHolder
});
}

Entity can't pass short (int16) values out

This is really holding up the process.
I have a db of marks stored as short, and want to extract a single entry out and create summary statistics on it. It seems simple but I seem to have to jump through a ton of hoops to get it so must be missing something basic.
Here is the basic method, and this works happily. however all i can do with it is pass it to the DataGridView.
private void MarksSummary(string StudentID)
{
int ID = Convert.ToInt32(StudentID);
//get the average of the marks using entity
using (var context = new collegestudentsEntities1())
{
var StudentMarks = (from m in context.Marks
where m.StudIDFK == ID
select new
{
m.Marks1,
m.marks2,
m.Marks3,
m.Marks4
});
dataGridView1.DataSource = StudentMarks.ToList();
Anything else, seems to be ridiculously long winded.
Eg: I can't do this
var Marklist = new List<Int16>();
StudentMarks.ToList().ForEach(m => Marklist.Add(m));
as I get "cannot convert from 'AnonymousType#1' to 'short'"
or this
Marklist = StudentMarks.ToList();
or this
double av = Marklist.Average();
Yet I can do a forEach which is silly on one row of data
foreach (var s in StudentMarks)
{
Marklist.Add(s.Marks1);
Marklist.Add(s.marks2);
Marklist.Add(s.Marks3);
Marklist.Add(s.Marks4);
}
and this works outputting happily
txtMarksOverFifty.Text = Marklist.Count(s => s > 50).ToString();
txtMarksFailed.Text = Marklist.Count(s => s < 50).ToString();
So what am I missing to get the values out of the query easily?
Thanks for your help :-)
Your foreach is trying to add an anonymous type
select new
{
m.Marks1,
m.marks2,
m.Marks3,
m.Marks4
} //...
To a List<Int16> so it's not surprising that fails. What it looks like you want to do with that is:
StudentMarks.ToList().ForEach(m => Marklist.AddRange(new [] { m.Marks1, m.marks2, m.Marks3, m.Marks4 }));
Edit: If you're just looking for a solution with less code you might try:
using (var context = new collegestudentsEntities1())
{
var StudentMarks = (from m in context.Marks
where m.StudIDFK == ID
select new[]
{
m.Marks1,
m.marks2,
m.Marks3,
m.Marks4
}).SelectMany(mark => mark).ToList();
}
or simply:
List<Int16> Marklist = context.Marks.Where(mark => mark.StudIDFK == ID)
.SelectMany(m => new [] { m.Marks1, m.marks2, m.Marks3, m.Marks4 })
.ToList();
Look at what you are creating here:
select new
{
m.Marks1,
m.marks2,
m.Marks3,
m.Marks4
});
This is an object that contains shorts.
StudentMarks.ToList().ForEach(m => Marklist.Add(m));
Here you are trying to add an object to a list of shorts. Try:
StudentMarks.ToList().ForEach(m => {
Marklist.Add(m.Mark1);
Marklist.Add(m.Mark2);
Marklist.Add(m.Mark3);
Marklist.Add(m.Mark4);
}
);

C# LINQ question about foreach

is there any way to write this foreach in linq or another better way,
int itemNr = -1;
foreach(ItemDoc itemDoc in handOverDoc.Assignment.Items) {
itemNr++;
foreach(ItemDetailDoc detail in itemDoc.Details) {
int eventDocNr = -1;
foreach(EventDoc eventDoc in detail.Events) {
eventDocNr++;
if(!eventDoc.HasEAN) {
HideShowPanels(pMatch);
txt_EAN.Text = String.Empty;
lbl_Match_ArtName.Text = itemDoc.Name;
lbl_ArtNr.Text = itemDoc.Number;
lbl_unitDesc.Text = eventDoc.Description;
m_tempItemNr = itemNr;
m_tempEventNr = eventDocNr;
txt_EAN.Focus();
return;
}
}
}
}
I just think this is not the correct way to write it. please advise.
If itemNr and eventDocNr is not needed you could use:
var item =
(from itemDoc in handOverDoc.Assignment.Items
from detail in itemDoc.Details
from eventDoc in detail.Events
where !eventDoc.HasEAN
select new
{
Name = itemDoc.Name,
Number = itemDoc.Number,
Description = eventDoc.Description
}).FirstOrDefault();
if (item != null)
{
HideShowPanels(pMatch);
txt_EAN.Text = String.Empty;
lbl_Match_ArtName.Text = item.Name;
lbl_ArtNr.Text = item.Number;
lbl_unitDesc.Text = item.Description;
txt_EAN.Focus();
}
No, I dont think there is a better way to do that. LINQ is about queries, you do quite a lot of processing in there. Unless you have a shortcut that is not obvious here.... this seems t obe the only way.
If you COULD start from the eventDoc - you could filter out those without EAN and then go from there backward, but Ican not say how feasible that is as I miss the complete model (as in: maybe you have no back lniks, so you would be stuck wit hthe eventDoc an dcoul dnot get up to the item.
First look that looks fine.
You could try the following LINQ:
var nonEANs = from ItemDoc itemDocs in itemDocList
from ItemDetailDoc itemDetailDocs in itemDocs.Details
from EventDoc eventDocs in itemDetailDocs.Events
where !eventDocs.HasEAN
select eventDocs;
foreach (var i in nonEANs)
{
System.Diagnostics.Debug.WriteLine( i.HasEAN);
}
Should return 7 false EANs: I recreated you nested structures like this
List<ItemDoc> itemDocList = new List<ItemDoc>()
{
new ItemDoc()
{
Details = new List<ItemDetailDoc>()
{
new ItemDetailDoc()
{
Events = new List<EventDoc>()
{
new EventDoc()
{HasEAN=false},
new EventDoc()
{HasEAN=false}
}
},
new ItemDetailDoc()
{
Events = new List<EventDoc>()
{
new EventDoc()
{HasEAN=true},
new EventDoc()
{HasEAN=false}
}
}
}
},
new ItemDoc()
{
Details = new List<ItemDetailDoc>()
{
new ItemDetailDoc()
{
Events = new List<EventDoc>()
{
new EventDoc()
{HasEAN=false},
new EventDoc()
{HasEAN=false}
}
},
new ItemDetailDoc()
{
Events = new List<EventDoc>()
{
new EventDoc()
{HasEAN=false},
new EventDoc()
{HasEAN=false}
}
}
}
}
};
I think you are stuck with the for each loops as you need the itemNr and eventDocNr. You can use for loops to avoid increasing the itemNr and eventDocNr, but this does not reduce the number of loops.
Edit:
And if you do need the itemNr and eventDocNr try this:
var query = handOverDoc.Assignment.Items
.SelectMany(
(x, i) => x.Details.SelectMany(
(d, di) => d.Events.Where(x => x.HasEAN).Select(
(e, ei) => new {
ItemIndex = di,
EventIndex = ei,
Detail = d,
Event = e
}
)
)
);
foreach (var eventInfo in query) {
HideShowPanels(pMatch);
txt_EAN.Text = String.Empty;
lbl_Match_ArtName.Text = eventInfo.Detail.Name;
lbl_ArtNr.Text = eventInfo.Detail.Number;
lbl_unitDesc.Text = eventInfo.Event.Description;
txt_EAN.Focus();
return;
}
If you need only the first event with an EAN you could also use the following on the above query:
var item = query.FirstOrDefault();
if (item != null) {
// do you stuff here
}
You can get the index in LINQ quite easily, for example :-
var itemDocs = handOverDoc.Assignment.Items.Select((h, i) => new { item = h, itemindex = i })
You can repeat this process for your inner loops also and I suspect you could then use SelectMany() to simplify it even further.
You're trying to do two different things here. Firstly you're trying to find a document, and secondly you're trying to change things based upon it. The first stage in the process is simply to clarify the code you already have, e.g.
(Note this takes into account previous comments that the computed indexes in the original code are not needed. The exact same type of split into two methods could be done whether or not the computed indexes are required, and it would still improve the original code.)
public void FindAndDisplayEventDocWithoutEAN(HandOverDoc handOverDoc)
{
var eventDoc = FindEventDocWithoutEAN(handOverDoc);
if (eventDoc != null)
{
Display(eventDoc);
}
}
public EventDoc FindEventDocWithoutEAN(HandOverDoc handOverDoc)
{
foreach(ItemDoc itemDoc in handOverDoc.Assignment.Items)
foreach(ItemDetailDoc detail in itemDoc.Details)
foreach(EventDoc eventDoc in detail.Events)
if(!eventDoc.HasEAN)
return eventDoc;
return null;
}
public void Display(EventDoc eventDoc)
{
HideShowPanels(pMatch);
txt_EAN.Text = String.Empty;
lbl_Match_ArtName.Text = itemDoc.Name;
lbl_ArtNr.Text = itemDoc.Number;
lbl_unitDesc.Text = eventDoc.Description;
m_tempItemNr = itemNr;
m_tempEventNr = eventDocNr;
txt_EAN.Focus();
}
Once you've done that, you should be able to see that one method is a query over the main document, and the other is a method to display the results of the query. This is what's known as the single responsibility principle, where each method does one thing, and is named after what it does.
The transformation of the nested foreach loops to a linq query is now almost trivial. Compare the method below with the method above, and you can see how mechanical it is to translate nested foreach loops into a linq query.
public EventDoc FindEventDocWithoutEAN(HandOverDoc handOverDoc)
{
return (from itemDoc in handOverDoc.Assignment.Items
from detail in itemDoc.Details
from eventDoc in detail.Events
where !eventDoc.HasEAN
select eventDoc).FirstOrDefault();
}
yet another spin...
var query = from itemDocVI in handOverDoc.Assignment
.Items
.Select((v, i) => new { v, i })
let itemDoc = itemDocVI.v
let itemNr = itemDocVI.i
from detail in itemDoc.Details
from eventDocVI in detail.Events
.Select((v, i) => new { v, i })
let eventDoc = eventDocVI.v
let eventDocNr = eventDocVI.i
where eventDoc.HasEAN
select new
{
itemDoc,
itemNr,
detail,
eventDoc,
eventDocNr
};
var item = query.FirstOrDefault();
if (item != null)
{
HideShowPanels(pMatch);
txt_EAN.Text = String.Empty;
lbl_Match_ArtName.Text = item.itemDoc.Name;
lbl_ArtNr.Text = item.itemDoc.Number;
lbl_unitDesc.Text = item.eventDoc.Description;
m_tempItemNr = item.itemNr;
m_tempEventNr = item.eventDocNr;
txt_EAN.Focus();
}

Categories