Related
I have a list
var list = new List<record>{
new record{ flashid = 450, position = 5, value = "a"},
new record{ flashid = 450, position = 6, value = "b"},
new record{ flashid = 450, position = 7, value = "c"},
new record{ flashid = 450, position = 7, value = "d"},
new record{ flashid = 450, position = 7, value = "e"},
new record{ flashid = 450, position = 8, value = "f"},
new record{ flashid = 450, position = 9, value = "g"}
}
I have 3 records with position as 7 with different value. from the above list, I need to create a string for each combination according to the position from 1 to 10. for missing position values we can have any temporary character.
So, the desired output will be
["....abcfg.","....abdfg.","....abefg."]
we can have any special character in the place of dot.
Thanks
Sure, you can solve this problem, as described at links from comments to your question, But, at this case, you should do tedious work - to write a lot of repeatable code like:
var answer = (from pos1 in list.Where(x => x.position == 1)
from pos2 in list.Where(x => x.position == 2)
from pos3 in list.Where(x => x.position == 3)
....
from pos10 in list.Where(x => x.position == 10)
select pos1.value + pos2.value + pos3.value + ... + pos10.value
).ToList();
So, each time, when you need to change number of possible positions you should add or remove corresponding lines of code. Instead, you can try recursive approach. Former solution is not dynamic - you should know number of positions in advance at compile time, whereas second solution can be simple tuned at run time via limit variable changing.
static int limit = 10;
static void Recursive(record record, List<string> bag,
Stack<record> stack, List<record> list)
{
stack.Push(record);
if (record.position == limit)
{
var temp = new StringBuilder();
foreach (var item in stack)
temp.Insert(0, item.value);
bag.Add(temp.ToString());
}
else
foreach (var item in list.Where(x => x.position == record.position + 1))
Recursive(item, bag, stack, list);
stack.Pop();
}
static List<string> Solve(List<record> list)
{
for (var i = 1; i <= limit; i++)
if (!list.Any(x => x.position == i))
list.Add(new record { position = i, value = "." });
var bag = new List<string>();
var stack = new Stack<record>();
foreach (var record in list.Where(x => x.position == 1))
Recursive(record, bag, stack, list);
return bag;
}
Usage:
var list = new List<record>
{
new record { flashid = 450, position = 5, value = "a"},
new record { flashid = 450, position = 6, value = "b"},
new record { flashid = 450, position = 7, value = "c"},
new record { flashid = 450, position = 7, value = "d"},
new record { flashid = 450, position = 7, value = "e"},
new record { flashid = 450, position = 8, value = "f"},
new record { flashid = 450, position = 9, value = "g"}
};
var answer = Solve(list);
Console.WriteLine("[" + string.Join(", ", answer) + "]");
//output: [....abcfg., ....abdfg., ....abefg.]
If you would like to have a pure LINQ solution you can try the following. First of all extend your record list so that it contains each missing position:
var extendedList = Enumerable
.Range(1, 10)
.GroupJoin(list, n => n, r => r.position, (n, g) => g
.DefaultIfEmpty(new record { flashid = 450, position = n, value = "." }));
This basically goes through range from 1 to 10 and for each number corresponding to position matches and groups records. If a group doesn't contain any matching value so that's when a position is missing there is created a default record with temporary character. So the result looks like this:
record[10][]
{
record[]
{
record { flashid=450, position=1, value="." }
},
(...)
record[]
{
record { flashid=450, position=5, value="a" }
},
(...)
record[]
{
record { flashid=450, position=7, value="c" },
record { flashid=450, position=7, value="d" },
record { flashid=450, position=7, value="e" }
},
(...)
},
Now to generate all possible values you can try this:
var result = extendedList
.Aggregate(Enumerable.Repeat("", 1), (a, c) => a
.SelectMany(b => c
.Select(d => b + d.value)));
Each Aggregate iteration transforms sequence of words into another sequence of words with added single character from characters group in current position. So it goes like this:
0. {""}
1. {"."}
2. {"."}
3. {".."}
4. {"..."}
5. {"....a"}
6. {"....ab"}
7. {"....abc", "....abd", "....abe"}
8. {"....abcf", "....abdf", "....abef"}
9. {"....abcfg", "....abdfg", "....abefg"}
10. {"....abcfg.", "....abdfg.", "....abefg."}
You can try it here: https://dotnetfiddle.net/JCgYFP
I have these two classes in my code and a List of Class2. I want to group the list elements by date and ID using LINQ.
public class Class1
{
public string ID;
public int y;
public int z;
}
public class Class2
{
public List<Class1> a;
public int p, q;
public string s;
public DateTime date;
}
My list is similar to this:
List<Class2> object1 = new List<Class2>
{
new Class2 {p = 5, q = 6, date = DateTime.Now, a = new List<Class1> { new Class1 { ID = "3643746HDJ", y = 0, z = 9 }, new Class1 { ID = "746327846HDJ", y = 0, z = 9 } } },
new Class2 {p = 5, q = 6, date = DateTime.Now.AddDays(1), a = new List<Class1> { new Class1 { ID = "3643746HDJ", y = 0, z = 9 }, new Class1 { ID = "746327846HDJ", y = 0, z = 9 } } },
new Class2 {p = 5, q = 6, date = DateTime.Now.AddDays(2), a = new List<Class1> { new Class1 { ID = "3643746HDJ", y = 0, z = 9 }, new Class1 { ID = "746327846HDJ", y = 0, z = 9 } } },
new Class2 {p = 5, q = 6, date = DateTime.Now.AddDays(3), a = new List<Class1> { new Class1 { ID = "3643746HDJ", y = 0, z = 9 }, new Class1 { ID = "746327846HDJ", y = 0, z = 9 } } },
};
Sorry if this is a stupid question, but I am a beginner in C# programming and LINQ and I am not sure if this is even possible.
This is what I have tried based on examples I saw on the web but its not correct
var newList = from item in object1
group new
{
item.s,
item.a[0].x, //I think its wrong because of this
item.a[0].y //and this
}
by new
{
item.date,
item.a[0].ID //and this
}
into temp
select temp;
Since I have hardcoded index 0 in the grouping I am missing a lot of elements in my final list. How can I do this for all elements of list a in Class2?
Expected output is similar to this:
Key: {date: 19-09-2017 ID: 3643746HDJ}, Element: {{s: "abc", y = 1, z = 2}, {s: "pqr", y = 2, z = 4}, {s: "abc", y = 1, z = 2}}
Key: {date: 20-09-2017 ID: 3643746HDJ}, Element: {{s: "pop", y = 1, z = 2}, {s: "dfr", y = 2, z = 4}, {s: "abc", y = 1, z = 2}}
Key: {date: 19-09-2017 ID: 746327846HDJ}, Element: {{s: "abc", y = 7, z = 8}, {s: "asar", y = 2, z = 111}, {s: "abc", y = 1, z = 2}}
Key: {date: 20-09-2017 ID: 746327846HDJ}, Element: {{s: "abc", y = 7, z = 8}, {s: "asar", y = 2, z = 111}, {s: "abc", y = 1, z = 2}}
Your question states:
Since I have hardcoded index 0 in the grouping I am missing a lot of elements in my final list. How can I do this for all elements of list a in Class2?
As you want all the items from the nested collections you need to flatten the nested collections before grouping. In query syntax do so using another from:
var result = from item in object1
from nested in item.a
group new { item.s, nested.y, nested.z } by new { item.date, nested.ID } into g
select g;
As each nested object does not have a p and q properties but your code shows accessing the nested items I used y and z instead
This can also be done using method syntax. To flatten the nested collections use SelectMany. For the GroupBy you can use the overload where you specify both key and element selector. General idea for that:
var result = object1.SelectMany(item => item.a.Select(nested => new {item, nested }))
.GroupBy(key => new { key.item.Date, key.nested.Id },
val => new { val.item.s, val.nested.x, val.nestd.y });
Even though it seems it is only for question purposes only I recommend giving meaningful names for properties and variables and to have a look at:
MSDN Naming Conventions
Dofactory
I realize my title probably isn't very clear so here's an example:
I have a list of objects with two properties, A and B.
public class Item
{
public int A { get; set; }
public int B { get; set; }
}
var list = new List<Item>
{
new Item() { A = 0, B = 0 },
new Item() { A = 0, B = 1 },
new Item() { A = 1, B = 0 },
new Item() { A = 2, B = 0 },
new Item() { A = 2, B = 1 },
new Item() { A = 2, B = 2 },
new Item() { A = 3, B = 0 },
new Item() { A = 3, B = 1 },
}
Using LINQ, what's the most elegant way to collapse all the A = 2 items into the first A = 2 item and return along with all the other items? This would be the expected result.
var list = new List<Item>
{
new Item() { A = 0, B = 0 },
new Item() { A = 0, B = 1 },
new Item() { A = 1, B = 0 },
new Item() { A = 2, B = 0 },
new Item() { A = 3, B = 0 },
new Item() { A = 3, B = 1 },
}
I'm not a LINQ expert and already have a "manual" solution but I really like the expressiveness of LINQ and was curious to see if it could be done better.
How about:
var collapsed = list.GroupBy(i => i.A)
.SelectMany(g => g.Key == 2 ? g.Take(1) : g);
The idea is to first group them by A and then select those again (flattening it with .SelectMany) but in the case of the Key being the one we want to collapse, we just take the first entry with Take(1).
One way you can accomplish this is with GroupBy. Group the items by A, and use a SelectMany to project each group into a flat list again. In the SelectMany, check if A is 2 and if so Take(1), otherwise return all results for that group. We're using Take instead of First because the result has to be IEnumerable.
var grouped = list.GroupBy(g => g.A);
var collapsed = grouped.SelectMany(g =>
{
if (g.Key == 2)
{
return g.Take(1);
}
return g;
});
One possible solution (if you insist on LINQ):
int a = 2;
var output = list.GroupBy(o => o.A == a ? a.ToString() : Guid.NewGuid().ToString())
.Select(g => g.First())
.ToList();
Group all items with A=2 into group with key equal to 2, but all other items will have unique group key (new guid), so you will have many groups having one item. Then from each group we take first item.
Yet another way:
var newlist = list.Where (l => l.A != 2 ).ToList();
newlist.Add( list.First (l => l.A == 2) );
An alternative to other answers based on GroupBy can be Aggregate:
// Aggregate lets iterate a sequence and accumulate a result (the first arg)
var list2 = list.Aggregate(new List<Item>(), (result, next) => {
// This will add the item in the source sequence either
// if A != 2 or, if it's A == 2, it will check that there's no A == 2
// already in the resulting sequence!
if(next.A != 2 || !result.Any(item => item.A == 2)) result.Add(next);
return result;
});
What about this:
list.RemoveAll(l => l.A == 2 && l != list.FirstOrDefault(i => i.A == 2));
if you whould like more efficient way it would be:
var first = list.FirstOrDefault(i => i.A == 2);
list.RemoveAll(l => l.A == 2 && l != first);
How can I find the set of items that occur in 2 or more sequences in a sequence of sequences?
In other words, I want the distinct values that occur in at least 2 of the passed in sequences.
Note:
This is not the intersect of all sequences but rather, the union of the intersect of all pairs of sequences.
Note 2:
The does not include the pair, or 2 combination, of a sequence with itself. That would be silly.
I have made an attempt myself,
public static IEnumerable<T> UnionOfIntersects<T>(
this IEnumerable<IEnumerable<T>> source)
{
var pairs =
from s1 in source
from s2 in source
select new { s1 , s2 };
var intersects = pairs
.Where(p => p.s1 != p.s2)
.Select(p => p.s1.Intersect(p.s2));
return intersects.SelectMany(i => i).Distinct();
}
but I'm concerned that this might be sub-optimal, I think it includes intersects of pair A, B and pair B, A which seems inefficient. I also think there might be a more efficient way to compound the sets as they are iterated.
I include some example input and output below:
{ { 1, 1, 2, 3, 4, 5, 7 }, { 5, 6, 7 }, { 2, 6, 7, 9 } , { 4 } }
returns
{ 2, 4, 5, 6, 7 }
and
{ { 1, 2, 3} } or { {} } or { }
returns
{ }
I'm looking for the best combination of readability and potential performance.
EDIT
I've performed some initial testing of the current answers, my code is here. Output below.
Original valid:True
DoomerOneLine valid:True
DoomerSqlLike valid:True
Svinja valid:True
Adricadar valid:True
Schmelter valid:True
Original 100000 iterations in 82ms
DoomerOneLine 100000 iterations in 58ms
DoomerSqlLike 100000 iterations in 82ms
Svinja 100000 iterations in 1039ms
Adricadar 100000 iterations in 879ms
Schmelter 100000 iterations in 9ms
At the moment, it looks as if Tim Schmelter's answer performs better by at least an order of magnitude.
// init sequences
var sequences = new int[][]
{
new int[] { 1, 2, 3, 4, 5, 7 },
new int[] { 5, 6, 7 },
new int[] { 2, 6, 7, 9 },
new int[] { 4 }
};
One-line way:
var result = sequences
.SelectMany(e => e.Distinct())
.GroupBy(e => e)
.Where(e => e.Count() > 1)
.Select(e => e.Key);
// result is { 2 4 5 7 6 }
Sql-like way (with ordering):
var result = (
from e in sequences.SelectMany(e => e.Distinct())
group e by e into g
where g.Count() > 1
orderby g.Key
select g.Key);
// result is { 2 4 5 6 7 }
May be fastest code (but not readable), complexity O(N):
var dic = new Dictionary<int, int>();
var subHash = new HashSet<int>();
int length = array.Length;
for (int i = 0; i < length; i++)
{
subHash.Clear();
int subLength = array[i].Length;
for (int j = 0; j < subLength; j++)
{
int n = array[i][j];
if (!subHash.Contains(n))
{
int counter;
if (dic.TryGetValue(n, out counter))
{
// duplicate
dic[n] = counter + 1;
}
else
{
// first occurance
dic[n] = 1;
}
}
else
{
// exclude duplucate in sub array
subHash.Add(n);
}
}
}
This should be very close to optimal - how "readable" it is depends on your taste. In my opinion it is also the most readable solution.
var seenElements = new HashSet<T>();
var repeatedElements = new HashSet<T>();
foreach (var list in source)
{
foreach (var element in list.Distinct())
{
if (seenElements.Contains(element))
{
repeatedElements.Add(element);
}
else
{
seenElements.Add(element);
}
}
}
return repeatedElements;
You can skip already Intesected sequences, this way will be a little faster.
public static IEnumerable<T> UnionOfIntersects<T>(this IEnumerable<IEnumerable<T>> source)
{
var result = new List<T>();
var sequences = source.ToList();
for (int sequenceIdx = 0; sequenceIdx < sequences.Count(); sequenceIdx++)
{
var sequence = sequences[sequenceIdx];
for (int targetSequenceIdx = sequenceIdx + 1; targetSequenceIdx < sequences.Count; targetSequenceIdx++)
{
var targetSequence = sequences[targetSequenceIdx];
var intersections = sequence.Intersect(targetSequence);
result.AddRange(intersections);
}
}
return result.Distinct();
}
How it works?
Input: {/*0*/ { 1, 2, 3, 4, 5, 7 } ,/*1*/ { 5, 6, 7 },/*2*/ { 2, 6, 7, 9 } , /*3*/{ 4 } }
Step 0: Intersect 0 with 1..3
Step 1: Intersect 1 with 2..3 (0 with 1 already has been intersected)
Step 2: Intersect 2 with 3 (0 with 2 and 1 with 2 already has been intersected)
Return: Distinct elements.
Result: { 2, 4, 5, 6, 7 }
You can test it with the below code
var lists = new List<List<int>>
{
new List<int> {1, 2, 3, 4, 5, 7},
new List<int> {5, 6, 7},
new List<int> {2, 6, 7, 9},
new List<int> {4 }
};
var result = lists.UnionOfIntersects();
You can try this approach, it might be more efficient and also allows to specify the minimum intersection-count and the comparer used:
public static IEnumerable<T> UnionOfIntersects<T>(this IEnumerable<IEnumerable<T>> source
, int minIntersectionCount
, IEqualityComparer<T> comparer = null)
{
if (comparer == null) comparer = EqualityComparer<T>.Default;
foreach (T item in source.SelectMany(s => s).Distinct(comparer))
{
int containedInHowManySequences = 0;
foreach (IEnumerable<T> seq in source)
{
bool contained = seq.Contains(item, comparer);
if (contained) containedInHowManySequences++;
if (containedInHowManySequences == minIntersectionCount)
{
yield return item;
break;
}
}
}
}
Some explaining words:
It enumerates all unique items in all sequences. Since Distinct is using a set this should be pretty efficient. That can help to speed up in case of many duplicates in all sequences.
The inner loop just looks into every sequence if the unique item is contained. Thefore it uses Enumerable.Contains which stops execution as soon as one item was found(so duplicates are no issue).
If the intersection-count reaches the minum intersection count this item is yielded and the next (unique) item is checked.
That should nail it:
int[][] test = { new int[] { 1, 2, 3, 4, 5, 7 }, new int[] { 5, 6, 7 }, new int[] { 2, 6, 7, 9 }, new int[] { 4 } };
var result = test.SelectMany(a => a.Distinct()).GroupBy(x => x).Where(g => g.Count() > 1).Select(y => y.Key).ToList();
First you make sure, there are no duplicates in each sequence. Then you join all sequences to a single sequence and look for duplicates as e.g. here.
Each key in dictionary has list of MANY integers. I need to iterate through each key and each time to take n items from list and do it until I iterate through all items in all lists. What is the best way to implement it? Do I need to implement some Enumerator?
The code:
enum ItemType { Type1=1, Type2=2, Type3=3 };
var items = new Dictionary<ItemType, List<int>>();
items[ItemType.Type1] = new List<int> { 1, 2, 3, 4, 5 };
items[ItemType.Type2] = new List<int> { 11, 12, 13, 15 };
items[ItemType.Type3] = new List<int> { 21, 22, 23, 24, 25, 26 };
For example: n=2.
1st iteration returns 1,2,11,12,21,22
2nd iteration returns 3,4,13,15,23,24
3rd iteration returns 5,25,26
UPDATED:
at the end I have to get list of this items in that order : 1,2,11,12,21,22, 3,4,13,15,23,24, 5,25,26
Here is how it might be done:
enum ItemType { Type1 = 1, Type2 = 2, Type3 = 3 };
Dictionary<ItemType, List<int>> items = new Dictionary<ItemType, List<int>>();
items[ItemType.Type1] = new List<int> { 1, 2, 3, 4, 5 };
items[ItemType.Type2] = new List<int> { 11, 12, 13, 15 };
items[ItemType.Type3] = new List<int> { 21, 22, 23, 24, 25, 26 };
// Define upper boundary of iteration
int max = items.Values.Select(v => v.Count).Max();
int i = 0, n = 2;
while (i + n <= max)
{
// Skip and Take - to select only next portion of elements, SelectMany - to merge resulting lists of portions
List<int> res = items.Values.Select(v => v.Skip(i).Take(n)).SelectMany(v => v).ToList();
i += n;
// Further processing of res
}
You don't need to define your custom enumerator, just use the MoveNext manually:
Step 1, convert you Dictionary<ItemType, List<int>> into Dictionary<ItemType, List<IEnumerator<int>>:
var iterators = items.ToDictionary(p => p.Key, p => (IEnumerator<int>)p.Value.GetEnumerator());
Step 2: handle MoveNext manually:
public List<int> Get(Dictionary<ItemType, IEnumerator<int>> iterators, int n)
{
var result = new List<int>();
foreach (var itor in iterators.Values)
{
for (var i = 0; i < n && itor.MoveNext(); i++)
{
result.Add(itor.Current);
}
}
return result;
}
Calling Get multiple times will give you the expected result. The enumerator itself will keep the current position.
This will do it for you:
var resultList = new List<int>();
items.ToList().ForEach(listInts => resultList.AddRange(listInts.Take(n));
This is letting LINQ extensions do the hard work for you. Take() will take as much as it can without throwing an exception if you request more than what there is. In this case I'm adding the results to another list, but you could just as easily tag another ForEach() on the end of the Take() in order to iterate the results.
I notice from the example sequences that you are retriving n number of items from x starting point - if you edit your question to include how the starting point is decided then I will adjust my example.
Edit:
Because you want to take n number of items from each list each iteration until there are no more elements returned, this will do it:
class Program
{
static void Main(string[] args)
{
var items = new Dictionary<ItemType, List<int>>();
items[ItemType.Type1] = new List<int> { 1, 2, 3, 4, 5 };
items[ItemType.Type2] = new List<int> { 11, 12, 13, 15 };
items[ItemType.Type3] = new List<int> { 21, 22, 23, 24, 25, 26 };
int numItemsTaken = 0;
var resultsList = new List<int>();
int n = 2, startpoint = 0, previousListSize = 0;
do
{
items.ToList().ForEach(x => resultsList.AddRange(x.Value.Skip(startpoint).Take(n)));
startpoint += n;
numItemsTaken = resultsList.Count - previousListSize;
previousListSize = resultsList.Count;
}
while (numItemsTaken > 0);
Console.WriteLine(string.Join(", ", resultsList));
Console.ReadKey();
}
enum ItemType { Type1 = 1, Type2 = 2, Type3 = 3 };
}
This is one of the few times you'll use a do while loop, and it will work regardless of the size of n or the size of your lists or how many lists there are.
The "best way" depends on your goal, e.g. readability or performance.
Here's one way:
var firstIter = items.Values.SelectMany(list => list.Take(2));
var secondIter = items.Values.SelectMany(list => list.Skip(2).Take(2));
var thirdIter = items.Values.SelectMany(list => list.Skip(4).Take(2));
var finalResult = firstIter.Concat(secondIter).Concat(thirdIter);
Edit: Here's a more general version:
var finalResult = Flatten(items, 0, 2);
IEnumerable<int> Flatten(
Dictionary<ItemType, List<int>> items,
int skipCount,
int takeCount)
{
var iter = items.Values.SelectMany(list => list.Skip(skipCount).Take(takeCount));
return
iter.Count() == 0 ? // a bit inefficient here
iter :
iter.Concat(Flatten(items, skipCount + takeCount, takeCount));
}