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 I have two arrays as follows
int[] first = { 1, 2, 3, 4, 5, 6, 12, 13, 14 };
int[] second = { 12, 13, 14, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 };
I want the result as follows:
matching index from the first = 6,7,8
matching index from second = 0,1,2
Condition: I cannot sort the array to find the index and there can be any number of the array.
I am looking for some efficient solution and I will be glad for the help.
Thanks in advance.
Below is the code I did for the two arrays:
class Program
{
static void Main(string[] args)
{
int[] first = { 1, 2, 3, 4, 5, 6, 12, 13, 14 };
int[] second = { 12, 13, 14, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 };
IndexArray sameIndexArray = CompareArray(first, second);
Console.WriteLine("FOLLOWING ARE THE INDEX WITH SAME VALUE FOR FIRST ARRAY");
foreach (var index in sameIndexArray.FirstArray)
{
Console.WriteLine(index);
}
Console.WriteLine("FOLLOWING ARE THE INDEX WITH SAME VALUE FOR SECOND ARRAY");
foreach (var index in sameIndexArray.SecondArray)
{
Console.WriteLine(index);
}
Console.ReadKey();
}
private static IndexArray CompareArray(int[] firstArray, int[] secondArray)
{
IndexArray arrayIndex = new IndexArray();
arrayIndex.FirstArray = new List<int>();
arrayIndex.SecondArray = new List<int>();
for (int i = 0; i < firstArray.Length; i++)
{
for (int j = 0; j < secondArray.Length; j++)
{
if (firstArray[i] == secondArray[j])
{
arrayIndex.FirstArray.Add(i);
arrayIndex.SecondArray.Add(j);
}
}
}
return arrayIndex;
}
}
public class IndexArray
{
public List<int> FirstArray { get; set; }
public List<int> SecondArray { get; set; }
}
Your solution is O(N^2). An O(N) or O(N log N) solution should be possible:
Create a HashSet for each of the sets
iterate over the first set, filtering by hashset2.Contains and print the indexes
do the same vice versa
Something like this:
private static IndexArray CompareArray(int[] firstArray, int[] secondArray)
{
IndexArray arrayIndex = new IndexArray();
var hashset2 = new HashSet<int>(secondArray);
for (int i = 0; i < firstArray.Length; i++)
{
if (hashset2.Contains(firstArray[i]))
arrayIndex.FirstArray.Add(i);
}
var hashset1 = new HashSet<int>(firstArray);
for (int i = 0; i < secondArray.Length; i++)
{
if (hashset1.Contains(secondArray[i]))
arrayIndex.SecondArray.Add(i);
}
return arrayIndex;
}
If this is working code it might be a better fit on code review.
I would drop the
arrayIndex.FirstArray = new List<int>();
arrayIndex.SecondArray = new List<int>();
Add
public List<int> FirstArray { get; } = new List<int>();
public List<int> SecondArray { get; } = new List<int>();
Arraylookup is fast but I would add
int first = firstArray[i];
And then use that.
WritelLine will write a line.
one a integer list and one a string list. The integer list's length will always be a multiple of 8. I would like to put the first 8 integers from my integer list into the first element of a string list, then loop and put the next 8 into the second element of the string list and so on. I have made an attempt, I currently have an error on the Add method as string doesn't have an add extension? Also I'm not sure if the way I have done it using loops is correct, any advice would be helpful.
List1 is my integer list
List2 is my string list
string x = "";
for (int i = 0; i < List1.Count/8; i++) {
for(int i2 = 0; i2 < i2+8; i2+=8)
{
x = Convert.ToString(List1[i2]);
List2[i].Add(h);
}
}
You can do that by using something like that
var list1 = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
var list2 = new List<string>();
for (int i = 0; i < list1.Count / 8; i++)
{
list2.Add(string.Concat(list1.Skip(i * 8).Take(8)));
}
// list2[0] = "12345678"
// list2[1] = "910111213141516"
A slightly more complicated approach, which only iterates once over list1 (would work with IEnumerable would be sth. like this:
var list1 = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }.AsEnumerable();
var list2 = new List<string>();
var i = 0;
var nextValue = new StringBuilder();
foreach (var integer in list1)
{
nextValue.Append(integer);
i++;
if (i != 0 && i % 8 == 0)
{
list2.Add(nextValue.ToString());
nextValue.Clear();
}
}
// could add remaining items if count of list1 is not a multiple of 8
// if (nextValue.Length > 0)
// {
// list2.Add(nextValue.ToString());
// }
For the fun of it, you can implement your own general purpose Batch extension method. Good practice to understand extension methods, enumerators, iterators, generics and c#'s local functions:
static IEnumerable<IEnumerable<T>> Batch<T>(
this IEnumerable<T> source,
int batchCount,
bool throwOnPartialBatch = false)
{
IEnumerable<T> nextBatch(IEnumerator<T> enumerator)
{
var counter = 0;
do
{
yield return enumerator.Current;
counter += 1;
} while (counter < batchCount && enumerator.MoveNext());
if (throwOnPartialBatch && counter != batchCount) //numers.Count % batchCount is not zero.
throw new InvalidOperationException("Invalid batch size.");
}
if (source == null)
throw new ArgumentNullException(nameof(source));
if (batchCount < 1)
throw new ArgumentOutOfRangeException(nameof(batchCount));
using (var e = source.GetEnumerator())
{
while (e.MoveNext())
{
yield return nextBatch(e);
}
}
}
Using it is rather trivial:
var ii = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
var ss = ii.Batch(4, true)
.Select(b => string.Join(", ", b))
And sure enough, the output is:
1, 2, 3, 4
5, 6, 7, 8
9, 10, 11, 12
while (listOfintergers.Count() > 0)
{
var first8elements = listOfintergers.ConvertAll(t=>t.ToString()).Take(8);
listOfStrings.Add(string.Concat(first8elements));
listOfintergers = listOfintergers.Skip(8).ToList();
}
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++;
}
}
Let's say I have a list of predefined numbers, and a list of predefined max limits.
When a user picks a limit, I need to randomly pick a certain amount of numbers from the first list, up until their totals match (As close to, but never over) the user selected total.
What I've tried so far:
void Main()
{
List<int> num = new List<int>(){ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,17, 18, 19, 20 };
int maxNum = 17;
List<int> curNum = new List<int>();
int curTotal = 0;
foreach(int sel in num.Where(x => x < maxNum)){
curTotal += sel;
if(curTotal <= maxNum){
curNum.Add(sel);
}
}
}
There needs to be x amount of numbers picked. In this case, 5 numbers picked, +- 20 numbers to be randomly picked from, and 1 max values.
So the end list should look like this:
1, 2, 3, 4, 7 (17)
1, 2, 3, 5, 6 (17)
1, 2, 3, 4, 6 (16) <- This will be fine if there isn't a solution to the max value.
Building upon #AlexiLevenkov's answer:
class Program
{
static void Main(string[] args)
{
int limit = 17;
int listSize = 5;
List<int> a = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 };
a.Shuffle();
List<int> genList = new List<int>();
int stoppedCount = 0;
for (int i = 0; i < a.Count(); i++)
{
if (i < listSize)
{
genList.Add(a[i]);
stoppedCount = i;
}
else
{
break;
}
}
while (genList.Sum() > limit)
{
genList.Remove(genList.Max());
stoppedCount++;
genList.Add(a[stoppedCount]);
}
}
}
static class ThisClass
{
public static void Shuffle<T>(this IList<T> list)
{
Random rng = new Random();
int n = list.Count;
while (n > 1)
{
n--;
int k = rng.Next(n + 1);
T value = list[k];
list[k] = list[n];
list[n] = value;
}
}
}
I think shuffle + "take while sum < limit" may be what you are looking for.
Something like following:
var shuffledList = num.ToList();
shuffledList.Shuffle();
var sum = 0;
var count = 0;
while (shuffledList[count] + sum < max)
{
sum += shuffledList[count++];
}
return shuffledList.Take(count);