Related
static List<int>Merge(List<int> list_a, List<int> list_b)
{
List<int> list_c=new List<int>();
int countA = 0, countB = 0;
for (int i =0;i< list_a.Count + list_b.Count-2;i++)
{
if (list_a[countA]<=list_b[countB])
{
list_c.Add(list_a[countA]);
countA ++;
}
else
{
list_c.Add(list_b[countB]);
countB ++;
}
}
return list_c;
}
my idea was to go through the for loop as many times as how many element list_c will have at the end
Compare each element in both list then add the smallest one in list_c
i already have a way to check if both lists are in ascending order
when im testing it with
List<int> myList1 = new List<int> { 1, 2, 3, 7, 8, 9 };
List<int> myList2 = new List<int> { 4, 5, 6};
Console.WriteLine("new list :{ " + string.Join(",", Merge(myList1, myList2)));
countB goes out of bound once the last element in list b is added, the next comparison in that for-loop is then invalid as its comparing list_b[3]
Your index on the shorter array is exceed its maximum index. Need to check whether a Count is exceed the maximum index.
class Program {
static List<int> Merge(List<int> list_a, List<int> list_b) {
List<int> list_c = new List<int>();
int countA = 0, countB = 0;
for (int i = 0; i < list_a.Count + list_b.Count; i++) {
if (countA < list_a.Count && countB < list_b.Count) {
if (list_a[countA] <= list_b[countB]) {
list_c.Add(list_a[countA]);
countA++;
}
else {
list_c.Add(list_b[countB]);
countB++;
}
}
else if (countA < list_a.Count) {
list_c.Add(list_a[countA]);
countA++;
}
else {
list_c.Add(list_b[countB]);
countB++;
}
}
return list_c;
}
static void Main(string[] args) {
List<int> myList1 = new List<int> { 1, 2, 3, 7, 8, 9 };
List<int> myList2 = new List<int> { 4, 5, 6 };
Console.WriteLine("new list :{ " + string.Join(",", Merge(myList1, myList2)) + "}");
}
}
Considering you want to use cycles:
public static List<int> Merge(List<int> list_a, List<int> list_b)
{
int firstListIndexer = 0, secondListIndexer = 0;
List<int> list_c = new List<int>();
// Traverse lists, until one of them run out of the elements
while (firstListIndexer < list_a.Count && secondListIndexer < list_b.Count)
{
if (list_a[firstListIndexer] < list_b[secondListIndexer])
list_c.Add(list_a[firstListIndexer++]);
else
list_c.Add(list_b[secondListIndexer++]);
}
// Store remaining elements of first list
while (firstListIndexer < list_a.Count)
list_c.Add(list_a[firstListIndexer++]);
// Store remaining elements of second list
while (secondListIndexer < list_b.Count)
list_c.Add(list_b[secondListIndexer++]);
return list_c;
}
Also, you can read this to improve your knowledge on the subject.
If we can assume that both lists are ascendingly ordered then you can merge the collections like this to respect ascending ordering.
static List<int> MergeTowAscendinglyOrderedCollections(IEnumerable<int> collectionA, IEnumerable<int> collectionB)
{
var result = new List<int>();
IEnumerator<int> iteratorA = collectionA.GetEnumerator();
IEnumerator<int> iteratorB = collectionB.GetEnumerator();
bool doesIteratorAHaveRemainingItem = iteratorA.MoveNext();
bool doesIteratorBHaveRemainingItem = iteratorB.MoveNext();
void SaveIteratorAsCurrentAndAdvanceIt()
{
result.Add(iteratorA.Current);
doesIteratorAHaveRemainingItem = iteratorA.MoveNext();
}
void SaveIteratorBsCurrentAndAdvanceIt()
{
result.Add(iteratorB.Current);
doesIteratorBHaveRemainingItem = iteratorB.MoveNext();
}
do
{
if (iteratorA.Current < iteratorB.Current)
{
if (doesIteratorAHaveRemainingItem) SaveIteratorAsCurrentAndAdvanceIt();
else SaveIteratorBsCurrentAndAdvanceIt();
}
else if (iteratorA.Current > iteratorB.Current)
{
if (doesIteratorBHaveRemainingItem) SaveIteratorBsCurrentAndAdvanceIt();
else SaveIteratorAsCurrentAndAdvanceIt();
}
else if (iteratorA.Current == iteratorB.Current)
{
SaveIteratorAsCurrentAndAdvanceIt();
SaveIteratorBsCurrentAndAdvanceIt();
}
} while (doesIteratorAHaveRemainingItem || doesIteratorBHaveRemainingItem);
return result;
}
In case of duplication I've added both numbers to the merged list but depending your business requirements you can adjust the code to omit one or both values from the result.
you can do a Union
List<int> myList1 = new List<int> { 1, 2, 3, 7, 8, 9 };
List<int> myList2 = new List<int> { 4, 5, 6};
var merged = myList1.Union(myList2).OrderBy(o=>o).ToList();
foreach(int number in merged)
Console.WriteLine(number);
output as follows
1
2
3
4
5
6
7
8
9
If you have to implement your serlf:
Implementation with no counters and no indexes in just a few lines using Coroutines:
class Program {
static void Main() {
List<int> l1 = new List<int>() { 9,8, 7, 5, 3, 1 };
List<int> l2 = new List<int>() {12 ,10, 8, 6, 4, 2 };
IEnumerable<int> res = MergeAscending(sl1, sl2);
foreach (int item in res) {
Console.Write($"{item},");
}
Console.Read();
}
static IEnumerable<T> MergeAscending<T>(IEnumerable<T> l1, IEnumerable<T> l2) where T : struct, IComparable, IComparable<T>, IConvertible, IEquatable<T>, IFormattable {
IEnumerator<T> e1 = l1.AsParallel().OrderBy(e => e).GetEnumerator();
IEnumerator<T> e2 = l2.AsParallel().OrderBy(e => e).GetEnumerator();
IEnumerator<T> longest; //to yield longest list remains
//First move needed to init first "Current"
e1.MoveNext();
e2.MoveNext();
//yields smaller current value and move its IEnumerable pointer
//breaks while loop if no more values in some Enumerable and mark the other one as longest
while (true) {
if (e1.Current.CompareTo(e2.Current) < 0) {
yield return e1.Current;
if (!e1.MoveNext()) { longest = e2; break; }
}
else {
yield return e2.Current;
if (!e2.MoveNext()) { longest = e1; break; }
}
}
//finish the longest Enumerator
do {
yield return longest.Current;
} while (longest.MoveNext());
}
}
Anyway, my recomendation is just as Siavash in the comments:
var merged = myList1.Union(myList2).AsParallel().OrderBy(e => e).ToList();
Suppose that I have a list of integer or whatever
List<int> motherlist = { 1, 1, 2, 5, 7, 2, 2, 2, 6, 1 }
Console.WriteLine(children.Count); // 10
I would like to find all duplicates and not remove them from the list but to distribute them across other lists so the final count of all childrens should be the same as motherlist:
List<List<int>> children = { { 1, 2, 5, 7, 6 }, { 1, 2 }, { 1, 2 }, { 2 }}
Console.WriteLine(children.Sum(l => l.Count())); // 10 same as mother
I tried so far a brute force approach by looping through all elements of mother, comparing the elements with all other elements and to check for duplicates, If duplicate found I add it to a list of buckets (List of Lists) and so forth until the last elements.
But the brute force approach takes 7 CPU seconds for only a mother list of 300 items.
I imagine that if I had 1000 items this would take forever.
Is there a faster way to do this in C# .NET ?
I suggest grouping duplicates and then loop taking into account size of the groups:
public static IEnumerable<List<T>> MyDo<T>(IEnumerable<T> source,
IEqualityComparer<T> comparer = null) {
if (null == source)
throw new ArgumentNullException(nameof(source));
var groups = new Dictionary<T, List<T>>(comparer ?? EqualityComparer<T>.Default);
int maxLength = 0;
foreach (T item in source) {
if (!groups.TryGetValue(item, out var list))
groups.Add(item, list = new List<T>());
list.Add(item);
maxLength = Math.Max(maxLength, list.Count);
}
for (int i = 0; i < maxLength; ++i) {
List<T> result = new List<T>();
foreach (var value in groups.Values)
if (i < value.Count)
result.Add(value[i]);
yield return result;
}
}
Demo:
int[] source = new int[] { 1, 1, 2, 5, 7, 2, 2, 2, 6, 1 };
var result = MyDo(source).ToList();
string report = string.Join(Environment.NewLine, result
.Select(line => $"[{string.Join(", ", line)}]"));
Console.Write(report);
Outcome:
[1, 2, 5, 7, 6]
[1, 2]
[1, 2]
[2]
Stress Demo:
Random random = new Random(1234); // seed, the results to be reproducible
// We don't want 1000 items be forever; let's try 1_000_000 items
int[] source = Enumerable
.Range(1, 1_000_000)
.Select(x => random.Next(1, 1000))
.ToArray();
Stopwatch sw = new Stopwatch();
sw.Start();
var result = MyDo(source).ToList();
sw.Stop();
Console.WriteLine($"Time: {sw.ElapsedMilliseconds} ms");
Outcome: (may vary from workstation to workstation)
Time: 50 ms
I would GroupBy the elements of the list, and then use the count of elements to know the number of sublists an element has to be added in
List<int> motherlist = new List<int> { 1, 1, 2, 5, 7, 2, 2, 2, 6, 1 };
var childrens = motherlist.GroupBy(x => x).OrderByDescending(x => x.Count());
var result = new List<List<int>>();
foreach (var children in childrens)
{
for (var i = 0; i < children.Count(); i++)
{
if (result.Count() <= i) result.Add(new List<int>());
result[i].Add(children.Key);
}
}
Console.WriteLine("{");
foreach (var res in result)
{
Console.WriteLine($"\t{{ { string.Join(", ", res) } }}");
}
Console.WriteLine("}");
This outputs :
{
{ 2, 1, 5, 7, 6 }
{ 2, 1 }
{ 2, 1 }
{ 2 }
}
Just a quick shot, but it seems to work quite well...
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApp2
{
class Program
{
static void Main(string[] args)
{
List<int> motherlist = new List<int> { 1, 1, 2, 5, 7, 2, 2, 2, 6, 1 };
var rnd = new Random(1);
for (int i = 0; i < 1000; i++)
{
motherlist.Add(rnd.Next(1, 200));
}
var resultLists = new List<IEnumerable<int>>();
while (motherlist.Any())
{
var subList = motherlist.Distinct().OrderBy(x => x).ToList();
subList.ForEach(x => motherlist.Remove(x));
resultLists.Add(subList);
}
}
}
}
You can use a Dictionary<int, int> to keep track of the number of occurrences of each element and build the child lists in a single iteration with O(n) time complexity(most of the time) and without any LINQ:
var motherlist = new List<int>() { 1, 1, 2, 5, 7, 2, 2, 2, 6, 1 };
var counts = new Dictionary<int, int>();
var children = new List<List<int>>();
foreach(var element in motherlist)
{
counts.TryGetValue(element, out int count);
counts[element] = ++count;
if (children.Count < count)
{
children.Add(new List<int>() { element });
}
else
{
children[count - 1].Add(element);
}
}
OUTPUT
{ 1, 2, 5, 7, 6 }
{ 1, 2 }
{ 2, 1 }
{ 2 }
I'd like to sort multiple lists (variable number of them) into single list, but keeping the specific order. For example:
List A: { 1,2,3,4,5 }
List B: { 6,7,8 }
List C: { 9,10,11,12 }
Result List: { 1,6,9,2,7,10,3,8,11,4,12,5 }
The only idea I got was to remove the first element from each list and put it into resulting set (and repeat until all lists are empty), but maybe there is a better way that doesn't require to create copy of each list and doesn't affect the original lists as well?
I suggest using IEnumerator<T> to enumerate lists while they have items:
private static IEnumerable<T> Merge<T>(params IEnumerable<T>[] sources) {
List<IEnumerator<T>> enums = sources
.Select(source => source.GetEnumerator())
.ToList();
try {
while (enums.Any()) {
for (int i = 0; i < enums.Count;)
if (enums[i].MoveNext()) {
yield return enums[i].Current;
i += 1;
}
else {
// exhausted, let's remove enumerator
enums[i].Dispose();
enums.RemoveAt(i);
}
}
}
finally {
foreach (var en in enums)
en.Dispose();
}
}
Test
List<int> A = new List<int>() { 1, 2, 3, 4, 5 };
List<int> B = new List<int>() { 6, 7, 8 };
List<int> C = new List<int>() { 9, 10, 11, 12 };
var result = Merge(A, B, C)
.ToList();
Console.Write(string.Join(", ", result));
The outcome is
1, 6, 9, 2, 7, 10, 3, 8, 11, 4, 12, 5
For more flexible use
public static string MergeArrays(params IList<int>[] items)
{
var result = new List<int>();
for (var i = 0; i < items.Max(x => x.Count); i++)
result.AddRange(from rowList in items where rowList.Count > i select rowList[i]);
return string.Join(",", result);
}
.
var a = new List<int>() { 1, 2, 3, 4, 5 };
var b = new List<int>() { 6, 7, 8 };
var c = new List<int>() { 9, 10, 11, 12, 0, 2, 1 };
var r = MergeArrays(a, b, c);
There is no sense in over complicating this in my opinion, why not use a simple for loop to accomplish what you need?
List<int> list1 = new List<int> { 1, 2, 3, 4, 5 };
List<int> list2 = new List<int> { 6, 7, 8 };
List<int> list3 = new List<int> { 9, 10, 11, 12 };
List<int> resultList = new List<int>();
for (int i = 0; i < list1.Count || i < list2.Count || i < list3.Count; i++)
{
if (i < list1.Count) resultList.Add(list1[i]);
if (i < list2.Count) resultList.Add(list2[i]);
if (i < list3.Count) resultList.Add(list3[i]);
}
Result: 1,6,9,2,7,10,3,8,11,4,12,5
Here's a fairly simple way. It was fun to write up anyway.
No, it isn't the best, but it works and you could expand it to suit your needs of using a List<List<int>> very easily.
//Using arrays for simplicity, you get the idea.
int[] A = { 1, 2, 3, 4, 5 };
int[] B = { 6, 7, 8 };
int[] C = { 9, 10, 11, 12 };
List<int> ResultSet = new List<int>();
//Determine this somehow. I'm doing this for simplicity.
int longest = 5;
for (int i = 0; i < longest; i++)
{
if (i < A.Length)
ResultSet.Add(A[i]);
if (i < B.Length)
ResultSet.Add(B[i]);
if (i < C.Length)
ResultSet.Add(C[i]);
}
//ResultSet contains: { 1, 6, 9, 2, 7, 10, 3, 8, 11, 4, 12, 5 }
As you can see, just pop this out into a method and loop through your lists of lists, properly determining the max length of all lists.
I'd go with:
static void Main(string[] args)
{
var a = new List<int>() { 1, 2, 3, 4, 5 };
var b = new List<int>() { 6, 7, 8 };
var c = new List<int>() { 9, 10, 11, 12 };
var abc = XYZ<int>(new[] { a, b, c }).ToList();
}
static IEnumerable<T> XYZ<T>(IEnumerable<IList<T>> lists)
{
if (lists == null)
throw new ArgumentNullException();
var finished = false;
for (int index = 0; !finished; index++)
{
finished = true;
foreach (var list in lists)
if (list.Count > index) // list != null (prior checking for count)
{
finished = false;
yield return list[index];
}
}
}
I had to use use IList to have indexer and Count. It doesn't creates anything (no enumerators, no lists, etc.), purely yield return.
For your problem I create static method, which can merge any collections as you want:
public static class CollectionsHandling
{
/// <summary>
/// Merge collections to one by index
/// </summary>
/// <typeparam name="T">Type of collection elements</typeparam>
/// <param name="collections">Merging Collections</param>
/// <returns>New collection {firsts items, second items...}</returns>
public static IEnumerable<T> Merge<T>(params IEnumerable<T>[] collections)
{
// Max length of sent collections
var maxLength = 0;
// Enumerators of all collections
var enumerators = new List<IEnumerator<T>>();
foreach (var item in collections)
{
maxLength = Math.Max(item.Count(), maxLength);
if(collections.Any())
enumerators.Add(item.GetEnumerator());
}
// Set enumerators to first item
enumerators.ForEach(e => e.MoveNext());
var result = new List<T>();
for (int i = 0; i < maxLength; i++)
{
// Add elements to result collection
enumerators.ForEach(e => result.Add(e.Current));
// Remobve enumerators, in which no longer have elements
enumerators = enumerators.Where(e => e.MoveNext()).ToList();
}
return result;
}
}
Example of using:
static void Main(string[] args)
{
var a = new List<int> { 1, 2, 3, 4, 5 };
var b = new List<int> { 6, 7, 8 };
var c = new List<int> { 9, 10, 11, 12 };
var result= CollectionsHandling.Merge(a, b, c);
}
When you understand how it works, it will be possible to reduce the method of smaller.
Shortest and probably slowest solution
int[] A = { 1, 2, 3, 4, 5 };
int[] B = { 6, 7, 8 };
int[] C = { 9, 10, 11, 12 };
var arrs = new[] { A, B, C };
var merged = Enumerable.Range(0, arrs.Max(a => a.Length))
.Select(x => arrs.Where(a=>a.Length>x).Select(a=>a[x]))
.SelectMany(x=>x)
.ToArray();
upd.
Another way to solve - I just refactored #Sinatr answer.
static IEnumerable<T> XYZ<T>(IEnumerable<IList<T>> lists)
{
if (lists == null)
throw new ArgumentNullException();
var index = 0;
while (lists.Any(l => l.Count > index))
{
foreach (var list in lists)
if (list.Count > index)
yield return list[index];
index++;
}
}
I want to load scenes randomly without repetition using c#. Any help would do.
Thanks.
int[] array = new int[] { 1, 2, 3, 4, 6, 8, 9, 10, 11, 12 };
List<int> list = new List<int>();
void Start()
{
list.AddRange(array);
}
int GetUniqueRandom(bool RemoveFromTheList)
{
if (list.Count == 0)
{
if (RemoveFromTheList)
{
list.AddRange(array);
}
else
{
return -1; // never repeat
}
}
int rand = Random.Range(0, 10);
int value = list[rand];
list.RemoveAt(rand); return value;
}
A nice clean way is to shuffle the array, then put all the elements in a stack. All you need to get a random element is to pop an item off the stack.
You will want to remove the list in the list of fields and replace with this;
Stack remainingScenes = new Stack();
Remove the content of the Start() method - you don't need it.
In your method to get a new number;
if (remainingScenes.Count == 0) {
int n = array.Length;
while (n > 1)
{
int k = rng.Next(n--);
T temp = array[n];
array[n] = array[k];
array[k] = temp;
}
foreach(var element in array) {
remainingScenes.Push(element);
}
}
return remainingScenes.Pop();
The shuffle method is from here.
Uhmmm, this looks very straightforward. Judging from your code, you only need a little modification to make it work..
List<int> list = new List<int>() { 1, 2, 3, 4, 6, 8, 9, 10, 11, 12 };
int GetUniqueRandom(bool RemoveFromTheList)
{
if (list.Count == 0)
{
return -1;//nothing in the list so return negative value
}
//generate random index from list
int randIndex = Random.Range(0, list.Count - 1);
int value = list[rand];
if(RemoveFromTheList)
{
list.RemoveAt(randIndex);
}
return value;
}
Try this:
int[] array = new int[] { 1, 2, 3, 4, 6, 8, 9, 10, 11, 12 };
Stack<int> stack = null;
Then initialize like this:
var rnd = new Random();
stack = new Stack<int>(array.OrderBy(x => rnd.Next()));
Now you just keep getting values from the stack until it is empty:
var value = stack.Pop();
I'm sure there is a more consise way of achieving this result? However I created the following method to work out the previous quarter from the current month or DateTime passed in.
void Main()
{
var q = Helpers.PreviousQuarter();
q.Dump();
}
public class Helpers
{
public static int PreviousQuarter(DateTime? date = null)
{
var quarters = new Dictionary<int, int[]>();
quarters.Add(1, new[] { 1, 2, 3 });
quarters.Add(2, new[] { 4, 5, 6 });
quarters.Add(3, new[] { 7, 8, 9 });
quarters.Add(4, new[] { 10, 11, 12 });
foreach (var q in quarters)
{
if (q.Value.Any(m=> m == (date.HasValue ? date.Value.Month : DateTime.Now.AddMonths(-3).Month)))
return q.Key;
}
throw new ArgumentException("Could not calulate quarter.");
}
}
private int PreviousQuarter(DateTime date)
{
return (int)Math.Ceiling((double)date.AddMonths(-3).Month / (double)3);
}
This should do the trick
public static int PreviousQuarter(DateTime? date = null)
{
int month;
if (date.HasValue){
month = date.Value.Month;
} else {
month = DateTime.Now.AddMonths(-3).Month)
}
float quarter = month / 4;
return (int)Math.Ceiling(quarter +0.1);
}
Your current code is returning current quarter for a date you can modify your method like:
public static int CurrentQuarter(DateTime? date = null)
{
return (((date ?? DateTime.Now.AddMonths(-3)).Month - 1) / 3) + 1;
}
If you want to calculate previous quarter then you can use a single array instead of a dictionary like:
public static int PreviousQuarter(DateTime? date = null)
{
int[] previousQuarter = new[] {4, 4, 4, 1, 1, 1, 2, 2, 2, 3, 3, 3};
return previousQuarter[(date ?? DateTime.Now.AddMonths(-3)).Month];
}