C# arrange the items by matching start and end value - c#

I have a input in this way,
Start= 2, End= 3
Start= 6, End= 7
Start= 3, End= 4
Start= 0, End= 1
Start= 4, End= 5
Start= 1, End= 2
Start= 6, End= 7
where I have to display the output that are sorted by their matching start- and end values. The first element are the object whose Start doesn't have any matching End values, and after that the records(Start) have the matching end values.
I needed to have the output in the following ways:
Start = 0, End = 1
Start = 1, End = 2
Start = 2, End = 3
Start = 3, End = 4
Start = 4, End = 5
Start = 5, End = 6
Start = 6, End = 7
What I have tried so far:
Created a List of Inputs
var inputs = new List
{
new Input{Start = 2, End = 3},
new Input{Start = 6, End = 7},
new Input{Start = 3, End = 4},
new Input{Start = 0, End = 1},
new Input{Start = 5, End = 6},
new Input{Start = 4, End = 5},
new Input{Start = 1, End = 2}
};
Get the list of all the Ends
var ends = inputs.Select(t => t.End);
Filter from the input where value of End matched with the Start of the next Objects.
var filtered = inputs.Where(t => ends.Any(to => to == t.Start)).ToList();
Get the list where Start doesn't have the matching ends:
var output = inputs.Where(t => !ends.Any(to => to == t.Start)).ToList();
Now filter the results as:
while (filtered.Count != 0)
{
var outs = filtered.Where(i => output.Any(l => i.Start == l.End)).FirstOrDefault();
output.Add(outs);
filtered.Remove(outs);
}
foreach (Input s in output)
{
Console.WriteLine($"Start = {s.Start}, End = {s.End}");
}
Dotnet fiddle:
I have somehow able to solve this problem with this approach. But I have a doubt that this is not a proper ways of solving. Because I have a repetitive filter for getting the matched and unmatched records and, I have also a doubt on the while loop implementation.
Any suggestion for a better approach of solving this problem?

If the End is always equals Start + 1. Then, you could take the max value of Start and with a simple for loop, you check, if exists, return value, if not, insert the new value.
Here is an extension method that would accept almost all type of collections or arrays :
public static class Extensions
{
public static IEnumerable<Input?> SortAndFill(this IEnumerable<Input?> source)
{
if(source == null) yield break;
var max = source?.Max(x=> x.Start) ?? 0;
if (max == 0) yield break;
max++;
for (int x = 0; x < max; x++)
{
var item = source.FirstOrDefault(s => s.Start == x);
if (item == null)
item = new Input(x, x + 1);
yield return item;
}
}
}
usage :
var result = inputs.SortAndFill().ToArray();

Related

Check if a set exactly includes a subset using Linq taking into account duplicates

var subset = new[] { 9, 3, 9 };
var superset = new[] { 9, 10, 5, 3, 3, 3 };
subset.All(s => superset.Contains(s))
This code would return true, because 9 is included in the superset,but only once, I want an implementation that would take into account the duplicates, so it would return false
My thought was that you could group both sets by count, then test that the super group list contained every key from the sub group list and, in each case, the super count was greater than or equal to the corresponding subcount. I think that I've achieved that with the following:
var subset = new[] { 9, 3, 9 };
var superset = new[] { 9, 10, 5, 3, 3, 3 };
var subGroups = subset.GroupBy(n => n).ToArray();
var superGroups = superset.GroupBy(n => n).ToArray();
var basicResult = subset.All(n => superset.Contains(n));
var advancedResult = subGroups.All(subg => superGroups.Any(supg => subg.Key == supg.Key && subg.Count() <= supg.Count()));
Console.WriteLine(basicResult);
Console.WriteLine(advancedResult);
I did a few extra tests and it seemed to work but you can test some additional data sets to be sure.
Here is another solution :
var subset = new[] { 9, 3, 9 };
var superset = new[] { 9, 10, 5, 3, 3, 3 };
var subsetGroup = subset.GroupBy(x => x).Select(x => new { key = x.Key, count = x.Count() });
var supersetDict = superset.GroupBy(x => x).ToDictionary(x => x.Key, y => y.Count());
Boolean results = subsetGroup.All(x => supersetDict[x.key] >= x.count);
This works for me:
var subsetLookup = subset.ToLookup(x => x);
var supersetLookup = superset.ToLookup(x => x);
bool flag =
subsetLookup
.All(x => supersetLookup[x.Key].Count() >= subsetLookup[x.Key].Count());
That's not how sets and set operations work. Sets cannot contain duplicates.
You should treat the two arrays not as sets, but as (unordered) sequences. A possible algorithm would be: make a list from the sequence superset, then remove one by one each element of the sequence subset from the list until you are unable to find such an element in the list.
bool IsSubList(IEnumerable<int> sub, IEnumerable<int> super)
{
var list = super.ToList();
foreach (var item in sub)
{
if (!list.Remove(item))
return false; // not found in list, so sub is not a "sub-list" of super
}
return true; // all elements of sub were found in super
}
var subset = new[] { 9, 3 };
var superset = new[] { 9, 10, 5, 3,1, 3, 3 };
var isSubSet = IsSubList(subset, superset);

return accumulated values with Aggregate?

I have a list of Int and would like to 'fold' it like: (((((0+1) + 2) + 3) + 4) +5)
and end up with with a list like the result variable.
var numbers = new List<int>() {1, 2, 3, 4, 5};
var sum = numbers.Aggregate((a, b) => a + b); // 15
var desiredResult = [1, 3, 6, 10, 15];
How can I update the Aggregate method to return the desired result?
The overloads to Aggregate all produce a single output an int. That's not what you're looking for. You want to project one array to another with a running sum. Something like this would work:
var runningSum = 0;
var sum = numbers.Select(i => runningSum += i);
sum =
[1 3 6 10 15]

Create Combinations according to the position in c# list

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

How to iterate through Dictionary<MyEnum, List<int>> and each time return part of values of each list?

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));
}

comparing two lists and removing missing numbers with C#

there are two lists:
List<int> list2 = new List<int>(new[] { 1, 2, 3, 5, 6 }); // missing: 0 and 4
List<int> list1 = new List<int>(new[] { 0, 1, 2, 3, 4, 5, 6 });
how do you compare two lists, find missing numbers in List1 and remove these numbers from List1? To be more precise, I need to find a way to specify starting and ending position for comparison.
I imagine that the proccess should be very similar to this:
Step 1.
int start_num = 3; // we know that comparisons starts at number 3
int start = list2.IndexOf(start_num); // we get index of Number (3)
int end = start + 2; // get ending position
int end_num = list2[end]; // get ending number (6)
now we've got positions of numbers (and numbers themselves) for comparison in List2 (3,5,6)
Step 2. To get positions of numbers in List1 for comparison - we can do the following:
int startlist1 = list1.IndexOf(start_num); // starting position
int endlist1 = list1.IndexOf(end_num); // ending position
the range is following: (3,4,5,6)
Step 3. Comparison. Tricky part starts here and I need a help with it
Basically now we need to compare list2 at (3,5,6) with list1 at (3,4,5,6). The missing number is "4".
// I have troubles with this step but the result will be:
int remove_it = 4; // or int []
Step 4. Odd number removal.
int remove_it = 4;
list1 = list1.Where(a => a != remove_it).ToList();
works great, but what will happen if we have 2 missing numbers? i.e.
int remove_it = 4 // becomes int[] remove_it = {4, 0}
Result As you have guessed the result is new List1, without number 4 in it.
richTextBox1.Text = "" + string.Join(",", list1.ToArray()); // output: 0,1,2,3,5,6
textBox1.Text = "" + start + " " + start_num; // output: 2 3
textBox3.Text = "" + end + " " + end_num; // output: 4 6
textBox2.Text = "" + startlist1; // output: 3
textBox4.Text = "" + endlist1; // output: 6
Can you guy help me out with Step 3 or point me out to the right direction?
Also, can you say what will happen if starting number(start_num) is the last number, but I need to get next two numbers? In example from above numbers were 3,5,6, but they should be no different than 5,6,0 or 6,0,1 or 0,1,2.
Just answering the first part:
var list3 = list1.Intersect(list2);
This will set list3 to { 0, 1, 2, 3, 4, 5, 6 } - { 0, 4 } = { 1, 2, 3, 5, 6 }
And a reaction to step 1:
int start_num = 3; // we know that comparisons starts at number 3
int start = list2.IndexOf(start_num); // we get index of Number (3)
int end = start + 2; // get ending position
From where do you get all those magic numbers (3, + 2 ) ?
I think you are over-thinking this, a lot.
var result = list1.Intersect(list2)
You can add a .ToList on the end if you really need the result to be a list.
List<int> list2 = new List<int>(new[] { 1, 2, 3, 5, 6 }); // missing: 0 and 4
List<int> list1 = new List<int>(new[] { 0, 1, 2, 3, 4, 5, 6 });
// find items in list 2 notin 1
var exceptions = list1.Except(list2);
// or are you really wanting to do a union? (unique numbers in both arrays)
var uniquenumberlist = list1.Union(list2);
// or are you wanting to find common numbers in both arrays
var commonnumberslist = list1.Intersect(list2);
maybe you should work with OrderedList instead of List...
Something like this:
list1.RemoveAll(l=> !list2.Contains(l));
To get the numbers that exist in list1 but not in list2, you use the Except extension method:
IEnumerable<int> missing = list1.Except(list2);
To loop through this result to remove them from list1, you have to realise the result, otherwise it will read from the list while you are changing it, and you get an exception:
List<int> missing = list1.Except(list2).ToList();
Now you can just remove them:
foreach (int number in missing) {
list1.Remove(number);
}
I'm not sure I understand your issue, and I hope the solution I give you to be good for you.
You have 2 lists:
List list2 = new List(new[] { 1, 2, 3, 5, 6 }); // missing: 0 and 4
List list1 = new List(new[] { 0, 1, 2, 3, 4, 5, 6 });
To remove from list1 all the missing numbers in list2 I suggest this solution:
Build a new list with missing numbers:
List diff = new List();
then put all the numbers you need to remove in this list. Now the remove process should be simple, just take all the elements you added in diff and remove from list2.
Did I understand correctly that algorithm is:
1) take first number in List 2 and find such number in List1,
2) then remove everything from list 1 until you find second number form list2 (5)
3) repeat step 2) for next number in list2.?
You can use Intersect in conjunction with Skip and Take to get the intersection logic combined with a range (here we ignore the fact 0 is missing as we skip it):
static void Main(string[] args)
{
var list1 = new List<int> { 1, 2, 3, 4, 5 };
var list2 = new List<int> { 0, 1, 2, 3, 5, 6 };
foreach (var i in list2.Skip(3).Take(3).Intersect(list1))
Console.WriteLine(i); // Outputs 3 then 5.
Console.Read();
}
Though if I'm being really honest, I'm not sure what is being asked - the only thing I'm certain on is the intersect part:
var list1 = new List<int> { 1, 2, 3, 4, 5 };
var list2 = new List<int> { 0, 1, 2, 3, 5, 6 };
foreach (var i in list2.Intersect(list1))
Console.WriteLine(i); // Outputs 1, 2, 3, 5.
ok, seems like I hadn't explained the problem well enough, sorry about it. Anyone interested can understand what I meant by looking at this code:
List<int> list2 = new List<int>() { 1, 2, 3, 5, 6 }; // missing: 0 and 4
List<int> list1 = new List<int>() { 0, 1, 2, 3, 4, 5, 6 };
int number = 3; // starting position
int indexer = list2.BinarySearch(number);
if (indexer < 0)
{
list2.Insert(~index, number); // don't look at this part
}
// get indexes of "starting position"
int index1 = list1.Select((item, i) => new { Item = item, Index = i }).First(x => x.Item == number).Index;
int index2 = list2.Select((item, i) => new { Item = item, Index = i }).First(x => x.Item == number).Index;
// reorder lists starting at "starting position"
List<int> reorderedList1 = list1.Skip(index1).Concat(list1.Take(index1)).ToList(); //main big
List<int> reorderedList2 = list2.Skip(index2).Concat(list2.Take(index2)).ToList(); // main small
int end = 2; // get ending position: 2 numbers to the right
int end_num = reorderedList2[end]; // get ending number
int endlist1 = reorderedList1.IndexOf(end_num); // ending position
//get lists for comparison
reorderedList2 = reorderedList2.Take(end + 1).ToList();
reorderedList1 = reorderedList1.Take(endlist1 + 1).ToList();
//compare lists
var list3 = reorderedList1.Except(reorderedList2).ToList();
if (list3.Count != 0)
{
foreach (int item in list3)
{
list1 = list1.Where(x => x != item).ToList(); // remove from list
}
}
// list1 is the result that I wanted to see
if there are any ways to optimize this code please inform me. cheers.

Categories