I am using c#.net desktop application
I have gridview that looks like this
ibengin iend code preferredText Affirmation tag codeScheme Value
1 10 Kitkat Yes Choc
11 15 Mars Yes Choc
16 20 Bounty Yes Choc
21 27 A1 Kitkat Yes Choc USA
28 32 Bounty Yes Choc
33 47 Bounty No Choc
48 61 A1 Kitkat Yes Choc USA
62 65 B7 Mars Yes Choc UK
66 77 Kitkat Yes Choc USA
78 81 Kitkat Yes Choc
I want it to be grouped as follows
Affirmation PreferredText Count Value CodingScheme Code Positions
Yes Kitkat 5 USA A1 1:10,21:27,48:61,66:77,78:81
Yes Mars 2 UK B7 11:15,62:65
Yes Bounty 2 16:20,28:32
No Bounty 1 33:47
I have almost finished it, but one problem remained.
I want to use the first value of codeScheme and code as shown above
Here is the code that I have done so far.
appreciate any help
var lstInfo = grdBreakDown.Rows.Cast<DataGridViewRow>()
.Where(x => !x.IsNewRow) // either..
.Where(x => x.Cells["Tag"].Value.ToString() == Tag) //..or or both
.GroupBy(x => new
{
grpAffirmation = x.Cells["Affirmation"].Value.ToString(),
grpPreferredText = x.Cells["preferredText"].Value.ToString(),
grpValue = (cbxValue.Checked ? x.Cells["Value"].Value.ToString() : "")
})
.Select(y => new
{
Affirmation = y.Key.grpAffirmation,
PreferredText = y.Key.grpPreferredText,
Count = y.Count(),//.ToString(),
Value = y.Key.grpValue,
Positions = string.Join(",", y.Select(i => i.Cells["ibegin"].Value.ToString() + ":" + i.Cells["iend"].Value.ToString()))
})
.OrderByDescending(y => y.Count)
.ToList();
I found the answer
var lstInfo = grdBreakDown.Rows.Cast<DataGridViewRow>()
.Where(x => !x.IsNewRow) // either..
.Where(x => x.Cells["Tag"].Value.ToString() == Tag) //..or or both
.GroupBy(x => new
{
grpAffirmation = x.Cells["Affirmation"].Value.ToString(),
//grpCodingScheme = x.Cells["codingScheme"].Value.ToString(),
//grpCode = x.Cells["code"].Value.ToString(),
grpPreferredText = x.Cells["preferredText"].Value.ToString(),
grpValue = (cbxValue.Checked ? x.Cells["Value"].Value.ToString() : "")
})
.Select(y => new
{
Affirmation = y.Key.grpAffirmation,
CodingScheme = string.Join(",", y.Select(i => i.Cells["codingScheme"].Value.ToString()).Distinct()).TrimEnd(','),
Code = string.Join(",", y.Select(i => i.Cells["code"].Value.ToString()).Distinct()).TrimEnd(','),
PreferredText = y.Key.grpPreferredText,
Count = y.Count(),//.ToString(),
Value = y.Key.grpValue,
Positions = string.Join(",", y.Select(i => i.Cells["ibegin"].Value.ToString() + ":" + i.Cells["iend"].Value.ToString()))
})
.OrderByDescending(y => y.Count)
.ToList();
Related
I have a generic list of strings, but I know from 12 to 12 items I have a Record. I Also have a Model that want to populate.
My primary code
for (int r = 2; r <= rows; r++)
{
for (int c = 3; c <= cols; c++)
{
try
{
list.Add(usedRange.Cells[r, c].Value2.ToString());
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
}
foreach (string item in list)
{
}
So in foreach (string item in list) I know 12 the 24 then 36 my record is there
The problem is this peace of code
foreach (string item in list)
{
p.LastName = item
}
I need a for in order to populate my Model with all 12 items, I'm stuck
You can also get item each 12 elements with a for by index like this:
for (var i = 0; i < list.Count; i+=12)
var p.LastName = list[i];
If you want to have your list in chunks of 12 elements, you can use the Chunk Linq extension which is new in C# 10. An equivalent is available in the library MoreLinq if you don't use C# 10, it has a different name: Batch.
// Creating a sample list of dummy elements
// There will be 100 strings from "0" to "99".
var MyList = Enumerable.Range(0, 100)
.Select(i => $"{i}");
// Creating a list of chunks containing 12 elements
var ChunkedElements = MyList
.Chunk(12);
// Using the chunked list.
foreach (var chunk in ChunkedElements)
{
// Do what you want with the 12 elements
Console.WriteLine("New Chunk");
foreach(var e in chunk)
{
Console.WriteLine(e);
}
}
Output:
New Chunk
0
1
2
3
4
5
6
7
8
9
10
11
New Chunk
12
13
14
15
16
17
18
19
20
21
22
23
New Chunk
24
25
26
27
28
29
30
31
32
33
34
35
New Chunk
36
37
38
39
40
41
42
43
44
45
46
47
New Chunk
48
49
50
51
52
53
54
55
56
57
58
59
New Chunk
60
61
62
63
64
65
66
67
68
69
70
71
New Chunk
72
73
74
75
76
77
78
79
80
81
82
83
New Chunk
84
85
86
87
88
89
90
91
92
93
94
95
New Chunk
96
97
98
99
If you only want every 12th element, here is a Linq alternative to the classical for loops. It is not better or worst, but can be useful depending on the context.
// Creating a sample list of dummy elements
// There will be 100 strings from "0" to "99".
var MyList = Enumerable.Range(0, 100)
.Select(i => $"{i}");
// Filtering the list to get only the 12th elements
var FilteredElements = MyList
.Where((e, i) => i % 12 == 0);
// Using the filtered list
foreach (var e in FilteredElements)
{
// Here do what you want with each element.
Console.WriteLine(e);
}
Output:
0
12
24
36
48
60
72
84
96
Explanation:
.Where((e, i) => i % 12 == 0) is where the work is done. The linq extension method Where has an overload that takes as argument a Func<TSource, Int32, bool>. See the doc. The second parameter is the index of the element of the collection.
So .Where((e, i) => i % 12 == 0) keeps only the elements of the collection for which the index i is divisible by 12 (i % 12 == 0). Hence the result containing only multiples of 12.
For now is like this but I'll change it to a smother approach.
for (var i = 0; i < list.Count; i += 13)
{
p.Add(new Participants()
{
LastName = list[0+i],
FirstName = list[1 + i],
AddressType = list[2 + i],
Email = list[3 + i],
Company = list[4 + i],
Phone = list[5 + i],
Street = list[6 + i],
ZipCode = list[7 + i],
City = list[8 + i],
Country = list[9 + i],
Percent = list[10 + i],
Points = list[11 + i],
Passed = list[12 + i],
});
}
I didn't know exactly how to ask this question better so I will try to explain it as best as I can.
Let's say I have one list of 20 strings myList1<string> and I have another string string ToCompare. Now each of the strings in the list as well as the string ToCompare have 8 words divided by empty spaces. I want to know how many times combination of any three words from string ToCompare in any possible order is to be found in the strings of myList1<string>. For an example:
This is the list (short version - example):
string1 = "AA BB CC DD EE FF GG HH";
string2 = "BB DD EE AA HH II JJ MM";
.......
string20 = "NN OO AA RR EE BB FF KK";
string ToCompare = "BB GG AA FF CC MM RR II";
Now I want to know how many times any combination of 3 words from ToCompare string is to be found in myList1<string>. To clarify futher three words from ToCompare "BB AA CC" are found in string1 of the list thus the counter for these 3 words would be 1. Another 3 words from ToCompare "BB AA II" are found in the string2 of myList1<string> but the counter here would be also 1 because it's not the same combination of words (I have "AA" and "BB" but also "II". They are not equal). Order of these 3 words doesn't matter, that means "AA BB CC" = "BB AA CC" = "CC BB AA". I want to know how many combinations of all (any) 3 words from ToCompare are found in myList1<string>. I hope it's clear what I mean.
Any help would be appreciated, I don't have a clue how to solve this. Thanks.
Example from Vanest:
List<string> source = new List<string>();
source.Add("2 4 6 8 10 12 14 99");
source.Add("16 18 20 22 24 26 28 102");
source.Add("33 6 97 38 50 34 87 88");
string ToCompare = "2 4 6 15 20 22 28 44";
The rest of the code is exacty the same, and the result:
Key = 2 4 6, Value = 2
Key = 2 4 20, Value = 1
Key = 2 4 22, Value = 1
Key = 2 4 28, Value = 1
Key = 2 6 20, Value = 1
Key = 2 6 22, Value = 1
Key = 2 6 28, Value = 1
Key = 2 20 22, Value = 1
Key = 2 20 28, Value = 1
Key = 2 22 28, Value = 1
Key = 4 6 20, Value = 1
Key = 4 6 22, Value = 1
Key = 4 6 28, Value = 1
Key = 4 20 22, Value = 1
Key = 4 20 28, Value = 1
Key = 4 22 28, Value = 1
Key = 6 20 22, Value = 1
Key = 6 20 28, Value = 1
Key = 6 22 28, Value = 1
Key = 20 22 28, Value = 1
As you can see there are combinations which not exist in the strings, and the value of the first combination is 2 but it comes only one time in the first string
I think this should suffice your ask,
List<string> source = new List<string>();
source.Add("AA BB CC DD EE FF GG HH");
source.Add("BB DD EE AA HH II JJ MM");
source.Add("NN OO AA RR EE BB FF KK");
string ToCompare = "BB GG AA FF CC MM RR II";
string word1, word2, word3, existingKey;
string[] compareList = ToCompare.Split(new string[] { " " }, StringSplitOptions.None);
Dictionary<string, int> ResultDictionary = new Dictionary<string, int>();
for (int i = 0; i < compareList.Length - 2; i++)
{
word1 = compareList[i];
for (int j = i + 1; j < compareList.Length - 1; j++)
{
word2 = compareList[j];
for (int z = j + 1; z < compareList.Length; z++)
{
word3 = compareList[z];
source.ForEach(x =>
{
if (x.Contains(word1) && x.Contains(word2) && x.Contains(word3))
{
existingKey = ResultDictionary.Keys.FirstOrDefault(y => y.Contains(word1) && y.Contains(word2) && y.Contains(word3));
if (string.IsNullOrEmpty(existingKey))
{
ResultDictionary.Add(word1 + " " + word2 + " " + word3, 1);
}
else
{
ResultDictionary[existingKey]++;
}
}
});
}
}
}
ResultDictionary will have the 3 word combinations that occur in myList1<string> with their count of occurrences. To get the total count, retrieve and add all the value fields from ResultDictionary.
EDIT:
Below snippet produces correct result with the given input,
List<string> source = new List<string>();
source.Add("2 4 6 8 10 12 14 99");
source.Add("16 18 20 22 24 26 28 102");
source.Add("33 6 97 38 50 34 87 88");
string ToCompare = "2 4 6 15 20 22 28 44";
string word1, word2, word3, existingKey;
string[] compareList = ToCompare.Split(new string[] { " " }, StringSplitOptions.None);
string[] sourceList, keywordList;
Dictionary<string, int> ResultDictionary = new Dictionary<string, int>();
source.ForEach(x =>
{
sourceList = x.Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries);
for (int i = 0; i < compareList.Length - 2; i++)
{
word1 = compareList[i];
for (int j = i + 1; j < compareList.Length - 1; j++)
{
word2 = compareList[j];
for (int z = j + 1; z < compareList.Length; z++)
{
word3 = compareList[z];
if (sourceList.Contains(word1) && sourceList.Contains(word2) && sourceList.Contains(word3))
{
existingKey = ResultDictionary.Keys.FirstOrDefault(y =>
{
keywordList = y.Split(new string[] { " " }, StringSplitOptions.None);
return keywordList.Contains(word1) && keywordList.Contains(word2) && keywordList.Contains(word3);
});
if (string.IsNullOrEmpty(existingKey))
{
ResultDictionary.Add(word1 + " " + word2 + " " + word3, 1);
}
else
{
ResultDictionary[existingKey]++;
}
}
}
}
}
});
Hope this helps...
I think this will do what you're asking for:
void Main()
{
var list =
new List<String>
{
"AA BB CC DD EE FF GG HH",
"BB DD EE AA HH II JJ MM",
"NN OO AA RR EE BB FF KK"
};
var toCompare = "BB GG AA FF CC MM RR II";
var permutations = CountPermutations(list, toCompare);
}
public Int32 CountPermutations(List<String> list, String compare)
{
var words = compare.Split(' ');
return list
.Select(l => l.Split(' '))
.Select(l => new { String = String.Join(" ", l), Count = l.Join(words, li => li, wi => wi, (li, wi) => li).Count()})
.Sum(x => x.Count - 3);
}
[edit: 2/20/2019]
You can use the following to get all the matches to each list item with the total number of unique combinations
void Main()
{
var list =
new List<String>
{
"AA BB CC DD EE FF GG HH",
"BB DD EE AA HH II JJ MM",
"NN OO AA RR EE BB FF KK",
"AA AA CC DD EE FF GG HH"
};
list.Select((l, i) => new { Index = i, Item = l }).ToList().ForEach(x => Console.WriteLine($"List Item{x.Index + 1}: {x.Item}"));
var toCompare = "BB GG AA FF CC MM RR II";
Console.WriteLine($"To Compare: {toCompare}");
Func<Int32, Int32> Factorial = x => x < 0 ? -1 : x == 0 || x == 1 ? 1 : Enumerable.Range(1, x).Aggregate((c, v) => c * v);
var words = toCompare.Split(' ');
var matches = list
// Get a list of the list items with all their parts
.Select(l => new { Parts = l.Split(' '), Original = l })
// Join each part from the to-compare item to each part of the list item
.Select(l => new { String = String.Join(" ", l), Matches = l.Parts.Join(words, li => li, wi => wi, (li, wi) => li), l.Original })
// Only consider items with at least 3 matches
.Where(l => l.Matches.Count() >= 3)
// Get the each item including how many parts matched and how many unique parts there are of each part
.Select(l => new { l.Original, Matches = String.Join(" ", l.Matches), Count = l.Matches.Count(), Groups = l.Matches.GroupBy(m => m).Select(m => m.Count()) })
// To calculate the unique combinations for each match use the following mathematical equation: match_count! / (frequency_part_1! * frequency_part_2! * ... * frequency_part_n!)
.Select(l => new { l.Original, l.Matches, Combinations = Factorial(l.Count) / l.Groups.Aggregate((c, v) => c * Factorial(v)) })
.ToList();
matches.ForEach(m => Console.WriteLine($"Original: {m.Original}, Matches: {m.Matches}, Combinations: {m.Combinations}"));
var totalUniqueCombinations = matches.Sum(x => x.Combinations);
Console.WriteLine($"Total Unique Combinations: {totalUniqueCombinations}");
}
How can order my list using Linq equals rank() over in SQL ?
For example rank is my List<Player>
class Player
{
public int Id;
public int RankNumber;
public int Points;
public int Name;
}
Original Rank list:
RankNumber Points Name Id
1 100 James 01
2 80 Mike 50
3 80 Jess 22
4 50 Jack 11
5 50 Paul 03
6 10 Monik 13
I need this Rank:
RankNumber Points Name Id
1 100 James 01
2 80 Mike 50
2 80 Jess 22
4 50 Jack 11
4 50 Paul 03
6 10 Monik 13
I don't think there is a good way to convert this directly to Linq to SQL but you could do this:
var rankedPlayers = players
.OrderByDescending(p => p.Points)
.Select((p, r) => new Player
{
Id = p.Id,
RankNumber = players.Where(pl => pl.Points > p.Points).Count() + 1,
Points = p.Points,
Name = p.Name
});
It gives you the correct output but will convert horribly and inefficiently to SQL. So I would suggest this modification which materialises the data to a list before creating the ranks:
var rankedPlayers = players
.OrderByDescending(p => p.Points)
.ToList() //<--- Add this
.Select((p, r) => new Player
{
Id = p.Id,
RankNumber = players.Where(pl => pl.Points > p.Points).Count() + 1,
Points = p.Points,
Name = p.Name
});
You can try below expression:
var newData = players
.OrderByDescending(x => x.Points)
.GroupBy(x => x.Points)
.SelectMany((x, index) => x.Select(y => new Player
{
Name = y.Name,
Points = y.Points,
RankNumber = index + 1,
Id = y.Id
}));
players contains IEnumerable of objects of type Player and newData contains ordered data with rank.
The datatable has 5 columns
Name Class Course Month Score
Alex C1 Math 12 90
Bob C1 Chem 11 91
Alex C2 Math 11 91
Alex C1 Math 11 89
Bob C1 Chem 12 97
Alex C1 Math 10 94
Alex C2 Chem 12 92
Bob C2 Math 12 94
And I wanna group (name, class) and fetch the max math score in just Nov and Dec, and the max chem score. Heres my query code
DataRow[] dr1 = dt.Select("Course = 'Math' AND Month > 10");
var result_one = dr1.AsEnumerable()
.GroupBy(r => new { Name = r.Field<string>("Name"), Class = r.Field<string>("Class") })
.Select(g => new
{
Name = g.Key.Name,
Class = g.Key.Class,
Max = g.Max(r => r.Field<int>("Score")),
Max_Month = g.FirstOrDefault(gg => gg.Field<int>("Score") == g.Max(r => r.Field<int>("Score"))).Field<int>("Month"),
}
).Distinct().ToList();
DataRow[] dr2 = dt.Select("Course = 'Chem'");
var result_two = dr2.AsEnumerable()
.GroupBy(r => new { Name = r.Field<string>("Name"), Class = r.Field<string>("Class") })
.Select(g => new
{
Name = g.Key.Name,
Class = g.Key.Class,
Max = g.Max(r => r.Field<int>("Score")),
Max_Month = g.FirstOrDefault(gg => gg.Field<int>("Score") == g.Max(r => r.Field<int>("Score"))).Field<int>("Month"),
}
).Distinct().ToList();
And I could output these 2 query results as this:
Name Class Math_Max_Month Math_Max
Alex C1 12 90
Alex C2 11 91
Bob C2 12 94
Name Class Chem_Max_Month Chem_Max
Bob C1 12 97
Alex C2 12 92
But how can I merge these 2 results into 1 output such as this:
Name Class Math_Max_Month Math_Max Chem_Max_Month Chem_Max
Alex C1 12 90 null null
Alex C2 11 91 12 92
Bob C1 null null 12 97
Bob C2 12 94 null null
I've tried to use result_one.Concat(result_two) and result_one.Union(result_two), but both are incorrect.
Alright, seems a bit complicated in your example. So i'll give you an answer on a int[] instead of DataRow[]
int[] first = new int[] { 3, 5, 6, 9, 12, 14, 18, 20, 25, 28 };
int[] second = new int[] { 30, 32, 34, 36, 38, 40, 42, 44, 46, 48 };
int[] result = first
.Concat(second)
.OrderBy(x => x)
.ToArray();
Output will be
// 3, 5, 6, 9, 12, 14, 18, 20, 25, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48
Console.Write(String.Join(", ", result));
theoretically this should work in your case, sense we're only dealing with arrays.
This works perfectly well for your code.,
DataRow[] dr1 = dtt.Select("Course = 'Math' AND Month > 10");
var result_one = dr1.AsEnumerable()
.GroupBy(r => new { Name = r.Field<string>("Name"), Class = r.Field<string>("Class") })
.Select(g => new
{
Name = g.Key.Name,
Class = g.Key.Class,
Max = g.Max(r => r.Field<int>("Score")),
Max_Month = g.FirstOrDefault(gg => gg.Field<int>("Score") == g.Max(r => r.Field<int>("Score"))).Field<int>("Month"),
}
).Distinct().ToList();
DataRow[] dr2 = dtt.Select("Course = 'Chem'");
var result_two = dr2.AsEnumerable()
.GroupBy(r => new { Name = r.Field<string>("Name"), Class = r.Field<string>("Class") })
.Select(g => new
{
Name = g.Key.Name,
Class = g.Key.Class,
Chem_Max = g.Max(r => r.Field<int>("Score")),
Chem_Max_Month = g.FirstOrDefault(gg => gg.Field<int>("Score") == g.Max(r => r.Field<int>("Score"))).Field<int>("Month"),
}
).Distinct().ToList();
Left Join...
var lstLeftJoin = (from a in result_one
join b in result_two
on new { a.Name, a.Class } equals new { b.Name, b.Class }
into gj
from subpet in gj.DefaultIfEmpty()
select new { a.Name, a.Class, Math_Max_Month = a.Max_Month, Math_Max = a.Max, Chem_Max_Month = (subpet == null ? 0 : subpet.Chem_Max_Month), Chem_Max = (subpet == null ? 0 : subpet.Chem_Max) }).ToList();
Right Join...
var lstRightJoin = (from a in result_two
join b in result_one
on new { a.Name, a.Class } equals new { b.Name, b.Class }
into gj
from subpet in gj.DefaultIfEmpty()
select new { a.Name, a.Class, Math_Max_Month = (subpet == null ? 0 : subpet.Max_Month), Math_Max = (subpet == null ? 0 : subpet.Max), a.Chem_Max_Month, a.Chem_Max }).ToList();
Finaly the Union...
var lstUnion = lstLeftJoin.Select(s => new { Name = s.Name, Class = s.Class, Math_Max_Month = s.Math_Max_Month, Math_Max = s.Math_Max, Chem_Max_Month = s.Chem_Max_Month, Chem_Max = s.Chem_Max }).Union(lstRightJoin.Select(s => new { Name = s.Name, Class = s.Class, Math_Max_Month = s.Math_Max_Month, Math_Max = s.Math_Max, Chem_Max_Month = s.Chem_Max_Month, Chem_Max = s.Chem_Max })).OrderBy(o => o.Name).ThenBy(c => c.Class).ToList();
RESULT
Name Class Math_Max_Month Math_Max Chem_Max_Month Chem_Max
Alex C1 12 90 null null
Alex C2 11 91 12 92
Bob C1 null null 12 97
Bob C2 12 94 null null
I have a List<Fields> collection where the class Fields has the following properties
(Name, Currency, Amount, Date, OtherList) where OtherList is a List
Sample values
Name Currency Amount Date F1 F2 F3
ABC XX 12 12 Jan 2013 X Y Z
BCA YY 11 12 Jan 2013 A B C
ABC XX 10 13 Jan 2013 X Y Z
Now OtherList object will have the values (X, Y, Z) for record1 and (A, B, C) for record2 et.el
What I need to do is apply an aggregation on the above list on the fields Name, Currency, F1, F2 and F3 so that the result is as follows.
Output
Name Currency Amount Date F1 F2 F3
ABC XX 22 13 Jan 2013 X Y Z
BCA YY 11 12 Jan 2013 A B C
Any idea how I can do this?
Thanks.
You can use LINQ:
var aggrList = list
.GroupBy(x => new { x.Name, x.Currency, x.F1, x.F2, x.F3 })
.Select(grp => new {
Name = grp.First().Name,
Currency = grp.First().Currency,
Amount = grp.Sum(x=>x.Amount),
Date = grp.Max(x=>x.Date),
F1 = grp.First().F1,
F2 = grp.First().F2,
F3 = grp.First().F3,
});
Try to group this way:
list.Where(x => x.OtherList != null && x.OtherList.Count >= 3 )
.GroupBy(x => new {F1 = x.OtherList[0], F2 = x.OtherList[1], F3 = x.OtherList[2]})
.Select(g => new
{
Name = g.First().Name,
Currency = g.First().Currency,
Amount = g.Sum(x => x.Amount),
Date = g.Max(x => x.Date),
F1 = g.Key.F1,
F2 = g.Key.F2,
F3 = g.Key.F3,
});
I guess for Date you will apply Max() function, for Amount - Sum(), but how you plan to apply aggregation for Name and Currency fields?