|
Thanks for the full response, Jorgen !
«Tell me and I forget. Teach me and I remember. Involve me and I learn.» Benjamin Franklin
|
|
|
|
|
Jörgen Andersson wrote: would consume
I suggest "may have to enumerate" as a more accurate word choice.
|
|
|
|
|
Yes, you're probably right.
I actually believe I owe Bill a proper answer.
|
|
|
|
|
|
You are bound to get a DivideByZeroInExceptionException
|
|
|
|
|
Luc?! 'Zat really you?
|
|
|
|
|
Yep. Sprinkling Seasons Exceptions.
|
|
|
|
|
Hey Luc, whaddup?
|
|
|
|
|
Hi Nish,
I'm just checking both sides of the moon. It seems rather dark either way...
|
|
|
|
|
Heheh
|
|
|
|
|
As others have said, this is the sort of thing that's better served using a procedural iterator method than trying to fudge it with LINQ.
Something like this would work, and would only require one pass through the source list:
public static IEnumerable<KeyValuePair<T1, List<T1>>> ToChunkedKvPList<T1>(this IEnumerable<T1> source, int chunksz)
{
if (source == null) throw new ArgumentNullException("source");
if (chunksz <= 0) throw new ArgumentOutOfRangeException("chunksz", "The chunk size must be greater than 0.");
return ToChunkedKvPListIterator(source, chunksz);
}
private static IEnumerable<KeyValuePair<T1, List<T1>>> ToChunkedKvPListIterator<T1>(IEnumerable<T1> source, int chunksz)
{
int currentChunkSize = 0;
T1 currentHeader = default(T1);
var currentChunk = new List<T1>(chunksz);
foreach (T1 item in source)
{
if (currentChunkSize == 0)
{
currentHeader = item;
}
else
{
currentChunk.Add(item);
}
currentChunkSize++;
if (currentChunkSize == chunksz)
{
yield return new KeyValuePair<T1, List<T1>>(currentHeader, currentChunk);
currentChunkSize = 0;
currentHeader = default(T1);
currentChunk = new List<T1>(chunksz);
}
}
if (currentChunkSize != 0)
{
yield return new KeyValuePair<T1, List<T1>>(currentHeader, currentChunk);
}
}
The only thing you lose is the exception if the source list count isn't a precise multiple of the chunk size, which would require multiple passes.
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
Very interesting, Richard, thanks ! I do not see using Linq as shown here as "fudging" in any sense of that word, but perhaps that word connotes something for you I am unaware of. As I said to Piebald, the example here was not meant to show all the fire-proofing/validation I would expect to see in "production code," but, of course, I am happy to see you included what you felt was needed.
My hypothetical take-away from your, and others', responses here is:
1. using the 'Count() method on an IEnumerable is either, or both:
a. expensive in terms of memory or time of computation
b. may "break" re-using that IEnumerable depending on what the IEnumerable base-Type is.
As time, and pending eye-surgery, permits, I'll look into those.
cheers, Bill
«Tell me and I forget. Teach me and I remember. Involve me and I learn.» Benjamin Franklin
|
|
|
|
|
LINQ is very good at doing specific things, but when you need to introduce modified closures to make it do what you need, I consider that a "fudge".
The basic Count() method[^] has special cases for ICollection and ICollection<T> ; for everything else, it has to iterate the entire list. As you say, depending on the source sequence, this could be expensive in terms of memory and/or time. It could even break if the source sequence is not deterministic.
The version that uses Count() would also fail to work with infinite sequences, whereas the version that doesn't would just produce an infinite sequence of transformed values:
IEnumerable<int> AskDeepThought()
{
while (true)
{
yield return 42;
}
}
var answers = AskDeepThought().ToChunkedKvPList().Take(42);
BillWoodruff wrote: pending eye-surgery
Hope it goes well.
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
Thanks, Richard,
I find myself struggling to understand what you refer to by "modified closures;" and, to imagine what an IEnumerable that was not "sequence deterministic" would be.
As you may have noticed from the edit to my original post here, I have verified that the code will work on both Stacks, and Queues, as-is.
I am aware that you have a deep understanding of Linq; have you ever thought about writing articles, or tip/tricks, for CP on Linq ? I find your Tips/Tricks are always very valuable.
cheers, Bill
«Tell me and I forget. Teach me and I remember. Involve me and I learn.» Benjamin Franklin
|
|
|
|
|
BillWoodruff wrote: modified closures
In your original method, you've captured / "closed over" the variables chunksz , listsz and ndx . In the lambda method you've passed to GroupBy , you modify the ndx variable. Hence it's a "modified closure".
It's not technically wrong, and the code will work as expected, but it "feels" icky. (Hence your post asking if there was a way to avoid it.)
BillWoodruff wrote: an IEnumerable that was not "sequence deterministic"
An example would be the value returned from the BlockingCollection(T).GetConsumingEnumerable method[^] - once it has returned a value, that value is removed from the collection. If you iterate twice, you'll get no items the second time.
using (var bc = new BlockingCollection<int>())
{
Task.Run(() =>
{
for (int i = 0; i < 10; i++)
{
bc.Add(i);
Thread.Sleep(100);
}
bc.CompleteAdding();
});
var list = bc.GetConsumingEnumerable();
Console.WriteLine("{0} items in the collection.", list.Count());
Console.WriteLine("Iterate the list:");
foreach (var item in list)
{
Console.WriteLine(item);
}
Console.WriteLine("Iterate again:");
foreach (var item in bc.GetConsumingEnumerable())
{
Console.WriteLine(item);
}
}
BillWoodruff wrote: have you ever thought about writing articles, or tip/tricks, for CP on Linq ?
Two problems with that: coming up with ideas that haven't already been done, and finding the time.
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
Thanks, Richards for the substantive response.Richard Deeming wrote: It's not technically wrong, and the code will work as expected, but it "feels" icky. (Hence your post asking if there was a way to avoid it.) My thought was that there was probably a way using some Linq operator that supports indexing natively, such as Select, which could be used ... I am not educated enough to feel the "ick"
'BlockingCollection: another .NET Type I've never had looked at.
cheers, Bill
«Tell me and I forget. Teach me and I remember. Involve me and I learn.» Benjamin Franklin
|
|
|
|
|
So something like this:
return source
.Select((item, index) => new
{
item,
chunk = index / chunksz
})
.GroupBy(
x => x.chunk,
x => x.item,
(key, grp) => KeyValuePair.Create(grp.First(), grp.Skip(1).ToList())
);
(Using the KeyValuePair factory method from this post[^].)
Of course, that still wouldn't work with an infinite sequence, as GroupBy has to iterate the entire sequence before returning anything.
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
That code is a Zen koan !
[cue sound of one-hand clapping]
very appreciated, Bill
«Tell me and I forget. Teach me and I remember. Involve me and I learn.» Benjamin Franklin
|
|
|
|
|
I have a button in a datagrid and another in the toolstrip bar, each one invoking exactly the same function ('Gravar') whose goal is to fill or update the datagrid whose columns are composed of text and other columns with checkboxes, but the one from the datagrid updates the checkboxes while the other doesn't. Why is this happening?
|
|
|
|
|
How would we know?
We can't see your screen, access your HDD, or read your mind, so we have no idea how you coded your method!
Bad command or file name. Bad, bad command! Sit! Stay! Staaaay...
|
|
|
|
|
In the following code, the button 'bt_gravar_click' which belongs to a toolstrip bar doesn´t fill correctly the check boxes whereas the button ' bt_preencher', which is a normal button in datagrid, does. The code is not well indented.
private void bt_gravar_Click(object sender, EventArgs e)
{
Gravar();
}
private void bt_preencher_Click(object sender, EventArgs e)
{
Gravar();
}
public void Gravar()
{
BD_Conexao();
if (grid_lic.CurrentRow.Cells[3].Value.ToString() != "" | grid_lic.CurrentRow.Cells[3].Value.ToString() == "True")
bloqueador = 1;
else
bloqueador = 0;
if (grid_lic.CurrentRow.Cells[7].Value.ToString() != "" | grid_lic.CurrentRow.Cells[7].Value.ToString() == "True")
EArtigo = 1;
else
EArtigo = 0;
string Cmd = "Select * from lojas where NIF ='" + grid_lic.CurrentRow.Cells[1].Value + "' and loja ='" + grid_lic.CurrentRow.Cells[2].Value + "'";
OdbcCommand cm2 = new OdbcCommand(Cmd, con);
OdbcDataReader dr = cm2.ExecuteReader();
string cmd = "insert into licenciamentoloja (bloqueador, EArtigo) values (?, ?)";
OdbcCommand cm = new OdbcCommand(cmd, con);
cm.Parameters.AddWithValue("@bloqueador", bloqueador);
cm.Parameters.AddWithValue("@EArtigo", EArtigo);
cm.ExecuteNonQuery();
Consulta()
}
public void Consulta()
{
con = new OdbcConnection("driver= {MySQL ODBC 5.1 Driver};server=xxx; database=licenciamento; uid=estagio; password=1234; option = 3 ");
con.Open();
OdbcCommand Command = con.CreateCommand();
Command.CommandText = "select licenciamentoloja.bloqueador, licenciamentoloja.Artigo from licenciamentoloja;"
Command.CommandType = CommandType.Text;
Command.Connection = con;
OdbcDataAdapter adapter = new OdbcDataAdapter();
adapter.SelectCommand = Command;
_ds = new DataSet();
dt = _ds.Tables["filtro"];
adapter.Fill(_ds, "filtro");
grid_lic.DataSource = _ds;
grid_lic.DataMember = _ds.Tables[0].TableName;
grid_lic.DataMember = "filtro";
}
public void BD_Conexao()
{
try
{
OdbcConnection con = new OdbcConnection("driver= {MySQL ODBC 5.1 Driver};server=xxx; database=licenciamento; uid=estagio; password=1234; option = 3 ");
con.Open();
}
catch (Exception ex)
{
Console.WriteLine("Erro na ligação à base de dados. \n{0}", ex.Message);
return;
}
}
|
|
|
|
|
Just because you are calling the same method, doesn't mean the conditions are the same.
In particular, does the grid_lic.CurrentRow reference what you think it does?
So start with the debugger. Put a breakpoint on the first line in the Gravar method, and start stepping through the code looking at exactly what happens, and comparing that with what you expected to happen. When the two differ, it should give you a little information as to what it at fault.
We can't do that for you: we don't have access to your database.
But...some of that code is rather odd. Your BD_Conexao method in particular does nothing at all that affects the outside world, as the local con variable masks the global one used by the calling routine.
Bad command or file name. Bad, bad command! Sit! Stay! Staaaay...
|
|
|
|
|
And how do you tkink am I suppose to find the cause of the problem with break points if the break point pointing to the value of checkboxes, shows me the wrong value when I use the button from the toolbar and the correct value when I use the button from the datagrid? Does this takes me to any conclusion? It just confirms that it is not filling the checkboxes correctly, not why this should be happening.
|
|
|
|
|
Member 11449447 wrote: It just confirms that it is not filling the checkboxes correctly, not why this should be happening. Actually, it confirms that there is a bug in your code, and the debugger will help you to identify where that bug is.
|
|
|
|
|
So now you know the value being returned is not correct. Now you have to use the debugger to find out why. It's a tool that allows you to inspect variable contents as the code is running, so USE IT! Does the code that populates the controls work correctly? You better go verify that.
Also, the debugger is a tool that is used to correct YOU. Your understanding of what the code is doing is usually wrong. The debugger helps you understand your code AND DATA better. In development work, chances are high that your code is written to handle perfect data but it falls on its face when it encounters something that isn't perfect. The debugger helps you find these cases and adjust your code.
|
|
|
|
|