Reordering an array in a customized way - c#

I have an array of items that prints to pdf in the following order.
Lets say for eg:
lines = {1, 2, 3,
4, 5, 6,
7, 8, 9,
10}
is the content of my array.
However I want to change the order of the items in the array to
{1, 4, 7,
2, 5, 8,
3, 6, 9,
10}
Then I pass this array to my print engine. Basically if there are more than 3 items in the array, my new code should reorder it.
Could somebody help me figuring out the logic for that.
Thanks

Order the lines by the modulus of the line index with the number of rows.
public static ICollection<T> Sort<T>(ICollection<T> lines, int columns)
{
var rows = lines.Count/columns;
if (rows == 0)
{
return lines;
}
return lines.Select((line, i) => new {line, i})
.OrderBy(item => item.i < columns*rows ? item.i%rows : rows)
.Select(item => item.line)
.ToList();
}
Edit: Alternatively you can use an iterator method and the list's indexer instead of LINQ:
public static IEnumerable<T> Sort<T>(IList<T> lines, int columns)
{
var rows = lines.Count/columns;
for (var i = 0; i < lines.Count; i++)
{
var index = rows > 0 && i < columns*rows
? (i%columns)*rows + i/columns
: i;
yield return lines[index];
}
}

Assuming "for linear array assuming every 9 elements form 3x3 matrix transpose each subsequence, keep remainder as-is":
// assuming T[] items;
var toTranspose = (items.Count() / 9) * 9;
var remap = new int[]{1, 4, 7, 2, 5, 8, 3, 6, 9 };
var result = Enumerable.Range(0, toTranspose)
.Select(pos => items[(pos / 9) * 9 + (remap[pos % 9] - 1)])
.Concat(items.Skip(toTranspose)
.ToArray();
Summary of code:
get number of items that need to be moved (which is number of groups of 9 items int numberOfGroup = Count()/9;, multiplied by group size)
have custom transformation in remap array (note that indexes copied as-is from sample and actually off-by-one hence -1 in computing index)
for each element index under toTranspose get source element from corresponding group and apply transformation with remap.
finally Concat the remainder.
Notes:
one can easily provide custom transformation or inline transposition if needed.
can't apply transformation to the last partial group as elements will have to go to non-existent positions.

Related

Get array of elements from list that sum to value [duplicate]

This question already has answers here:
Finding all possible combinations of numbers to reach a given sum
(32 answers)
Closed 1 year ago.
Below is a simplified version of my question, the background summary underneath provides a greater context.
Problem:
Create a function that lists all elements from a list that sum to a given value in an array.
Given:
List<int> list = new List<int>() { 1, 2, 3, 4, 5, 9 };
If the value provided was 10, then the function should return an array with lists 1, 2, 3, 4, and 1, 4, 5, and 2, 3, 5, and 1, 9.
Background:
I'm trying to digitize an old card game called Pastra (or Bastra) and the objective is to collect cards from the board. You can only collect cards that match the number value of a card or any numbered cards that sum to the played card's number. Ace's are 1s.
I already have the code for collecting the card that has an equal value.
What I need is to create an array of values from the original list with the elements that sum to the given value.
I'll need to know which list is larger as well as to know which list contains which cards so that the same card is not collected more than once. (Note: This is out of scope for this question. I want to discover that myself, however it's here to provide context as to why I need the information this way).
Example:
Same as above, if the board has an Ace, 2, 3, 4, 5, and 9, if I were to play a 10, I could collect Ace, 2, 3, 4, or Ace, 4, 5, or 2, 3, 5, or Ace, 9.
Thank you for your help, it's greatly appreciated.
Here is a recursive solution that finds all the combinations of positive numbers. It won't remove duplicate combinations if the set of numbers includes duplicate numbers.
IEnumerable<IReadOnlyList<int>> FindCombosThatAddToValue(IReadOnlyList<int> numbers, int value)
{
var indices = new BitArray(numbers.Count);
var combos = new List<IReadOnlyList<int>>();
FindCombos(0, 0);
return combos;
void FindCombos(int index, int total)
{
if (index >= numbers.Count)
return;
var n = numbers[index];
var newTotal = total + n;
if (newTotal == value)
{
// this is a matching combo so lets return it
var combo = new List<int>();
for (int i = 0; i < index; i++)
{
if (indices[i])
{
combo.Add(numbers[i]);
}
}
combo.Add(n);
combos.Add(combo);
}
else
{
if (newTotal < value)
{
// try for more including this number/index
indices.Set(index, true); // index included in total
FindCombos(index + 1, newTotal);
}
// try for more not including this number/index
indices.Set(index, false); // index not included in total
FindCombos(index + 1, total);
}
}
}
You first need to create combinations, then filter the list based on the sum
Given
This is basically a generic method that uses a bit mask to figure out what combination have been visited, i.e.. A 30 element array will occupy a number with 30 bits, it increments the number to make combinations of those bits... for every bit pattern it will return a combination of the original array
Note : This can be used with long or BigInteger if needed
public static IEnumerable<T[]> GetCombinations<T>(T[] source)
{
for (var i = 0; i < (1 << source.Length); i++)
yield return source
.Where((t, j) => (i & (1 << j)) != 0)
.ToArray();
}
Filter
Not much to say here, filter the combinations based on Sum
public static IEnumerable<int[]> GetItems(IEnumerable<int> source, int target)
=> GetCombinations(source.ToArray())
.Where(items => items.Sum() == target);
Usage
List<int> list = new List<int>() { 1, 2, 3, 4, 5, 9 };
foreach (var found in GetItems(list,10))
Console.WriteLine(string.Join(", ", found));
Output
1, 2, 3, 4
2, 3, 5
1, 4, 5
1, 9

How to find the column count of list of lists in C#?

public static void matrix(List<List<int>> matrix)
{
//matrix.Count is for amount of Row here
}
Example
Here the jagged array is
{{ 1, 2, 3},
{ 4, 5, 6},
{ 7, 8, 9},
{10, 11, 12}}
Then matrix.Count gives 4.
Here I want column count.
If this is array instead of List then I can use matrix.GetLength(0) and matrix.GetLength(1) to find Row count and Column count?
In general case, since you have jagged structure you can define different ColCount:
{
{ 1, 2, 3 }, // Average Columns - 3
{ 4, 5, 6, 7}, // Max Columns - 4
{ 8, 9}, // Min Columns - 2
}
Assuming that null list has 0 columns you can put:
using System.Linq;
...
int minColCount = matrix.Min(list => list?.Count ?? 0);
int maxColCount = matrix.Max(list => list?.Count ?? 0);
int avgColCount = (int)Math.Round(matrix.Average(list => list?.Count ?? 0));
If you can guarantee that matrix is rectangular and doesn't contain null, you can put
int colCount = matrix.Count > 0 ? matrix[0].Count : 0;
You are using a list of list of int. In this situation, there is no guaranty that you have a matrix with fix column size.
For example { {1}, {4,5,6}, {7,8}, {10,11,12,13} } is a possible combination.
But, if you are sure inside lists have the same size you can get the first list's size.
int column = matrix?.FirstOrDefault()?.Count ?? -1;
Don't forget to add using System.Linq; at top of your code.

How to convert a sequence of points to a sequence of ranges?

For example, I have a sequence of points
List points = new List {0, 1, 2, 4, 5 ,7};
And I want to convert it to a sequence of ranges (My type Range(leftPoint, rightPoint)). For the example, results are
List<Range> ranges: {0, 1} {1, 2} {2, 4} {4, 5} {5, 7}
You could use LINQ (presuming the list is already sorted):
List<Range> rangeList = Enumerable.Range(0, points.Count - 1)
.Select(i => new Range(points[i], points[i + 1]))
.ToList();
Why not just use a simple for-loop?
for(var i = 0; i < points.Count() - 1; i++)
ranges.Add(new Range(points[i], points[i+1]))
List<int> points = new List<int> { 0, 1, 2, 4, 5, 7 };
List<List<int>> listOfRanges = new List<List<int>>();
listOfRanges.Add(points.GetRange(0, 2));
listOfRanges.Add(points.GetRange(1, 2));
listOfRanges.Add(points.GetRange(2, 2));
listOfRanges.Add(points.GetRange(3, 2));
listOfRanges.Add(points.GetRange(4, 2));
You can iterate over it like so:
List<int> points = new List {0, 1, 2, 4, 5 ,7};
List<Range> listOfRanges = new List<Range>();
int index = 0
foreach (int value in points) {
listOfRanges.add(new Range(points[i], points[i+1]));
index++;
}
You might get a null comparison exception on the last iteration of the loop as points[i+1] doesn't exist - if so just handle this with a simple if statement.
This is assuming by points you mean a list of integers. I'll update if I find you meant something different.
You can use LINQ to zip the two lists that we get when we:
Take everything except the last element
Take everything except the first element
These correspond to (in the case of your example):
0, 1, 2, 4, 5
1, 2, 4, 5 ,7
Here is how you do it in code:
var result =
points
.Take(points.Count - 1) //points except last element
.Zip(
points.Skip(1), //points except first element
(l, r) => new Range(l, r))
.ToList();

How to find all instances of mirrored duplicates?

I have been searching for some time now for any answers on how to do this.
What I am trying to do is, take an array of numbers, e.g. {1, 3, 5, 6, 8, 7, 6 ,5, 3, 1} (but it will use user input) and find the duplicates of these numbers that are mirrored and return how many indexes are involved in just one instance of said array.
I know the basics of C# but can't grasp this task. No, this is not homework. This is my own project to further my knowledge.
I am not currently around the code I have for parts of this, but would really appreciate any help/advice anyone could give me.
int[] array = {1, 3, 5, 6, 8, 7, 6 ,5, 3, 1};
//holds left index of mirrored pair, you can easily find the right one
var mirroredIndexes = new List<int>();
var length = array.Length;
for (int i = 0; i < length / 2; i++)
{
if(array[i] == array[length - i - 1])
mirroredIndexes.Add(i);
}
mirroredIndexes.ForEach(Console.WriteLine);
Console.WriteLine ("total of {0} mirrored pairs ({1})",
mirroredIndexes.Count,
string.Join(", ", mirroredIndexes.Select(i => array[i])));
prints next indexes:
0
1
2
3
total of 4 mirrored pairs (1, 3, 5, 6)
I think this is what you are after. This will return a list of matching indices.
Eg. first == last, second == second to last, third == third to last
var matches = new List<Tuple<int, int>>();
var array = new [] { 0, 1, 2, 3, 4, 5, 3, 2, 1, 0 };
if (array.Length % 2 != 0)
throw new Exception("Array must have an even amount of elements");
for (int i = 0; i < array.Length / 2; i++)
{
if (array[i] == array[array.Length - 1 - i])
{
matches.Add(new Tuple<int, int>(i, array.Length - 1 - i));
}
}
var firstMatchingIndex1 = matches[0].Item1;
// This will be 0
var firstMatchingIndex2 = matches[0].Item2;
// This will be 9
You could go further, using a custom class, and capture the actual value that matched (eg. index1 is 1, index2 is 8 and the value was 1.

Compare two List<int>

I am writing a small program to compare two List. If the values are the same, I add them to list dups, if they are different, I add them to distinct. I noticed that some of my values are added and some are not, and after debugging for awhile, I am not certain what the problem is. Can someone shed a little light? Thanks.
List<int> groupA = new List<int>();
List<int> groupB = new List<int>();
List<int> dups = new List<int>();
List<int> distinct = new List<int>();
groupA.Add(2);
groupA.Add(24);
groupA.Add(5);
groupA.Add(72);
groupA.Add(276);
groupA.Add(42);
groupA.Add(92);
groupA.Add(95);
groupA.Add(266);
groupA.Add(42);
groupA.Add(92);
groupB.Add(5);
groupB.Add(42);
groupB.Add(95);
groupA.Sort();
groupB.Sort();
for (int a = 0; a < groupA.Count; a++)
{
for (int b = 0; b < groupB.Count; b++)
{
groupA[a].CompareTo(groupB[b]);
if (groupA[a] == groupB[b])
{
dups.Add(groupA[a]);
groupA.Remove(groupA[a]);
groupB.Remove(groupB[b]);
}
}
distinct.Add(groupA[a]);
}
I would use the Intersect and Except methods:
dups = groupA.Intersect(groupB).ToList();
distinct = groupA.Except(groupB).ToList();
When you remove an item from a list, you move the index of the remaining element down.
In essence, you are skipping some items using a for loop.
Try using a while loop, and manually increment the counter when you are not deleting an item.
For example, the following code is incorrect
List<int> nums = new List<int>{2, 4, 6, 7, 8, 10, 11};
for (int i = 0; i < nums.Count; i++)
{
if (nums[i] % 2 == 0)
nums.Remove(nums[i]);
}
If will return the list {4, 7, 10, 11} instead of just {7, 11}.
It will not remove the value of 4, because, when I remove the value of 2, (for i=0) the nums list goes from
//index 0 1 2 3 4 5 6
nums = {2, 4, 6, 7, 8, 10, 11}
to
//index 0 1 2 3 4 5
nums = {4, 6, 7, 8, 10, 11}
The loop finishes, the i is incremented to 1, and the next item referenced is nums[1], which is not 4 as one would intuitively expect, but 6. So in effect the value of 4 is skipped, and the check is not executed.
You should be very, very careful each time when you are modifying the collection you are iterating. For example, the foreach statement will throw an exception if you even try this. In this case you could use a while like
List<int> nums = new List<int>{2, 4, 6, 7, 8, 10, 11};
int i = 0;
while (i < nums.Count)
{
if (nums[i] % 2 == 0)
{
nums.Remove(nums[i])
}
else
{
i++; //only increment if you are not removing an item
//otherwise re-run the loop for the same value of i
}
}
of you could even fork the for, like
for (int i = 0; i < nums.Count; i++)
{
if (nums[i] % 2 == 0)
{
nums.Remove(nums[i]);
i--; //decrement the counter, so that it will stay in place
//when it is incremented at the end of the loop
}
}
Alternatively you could use linq, like this:
distinct.AddRange(groupA);
distinct.AddRange(groupB);
distinct = distinct.Distinct().ToList();
and
dups.AddRange(groupA);
dups.AddRange(groupB);
dups = dups.GroupBy(i => i)
.Where(g => g.Count() > 1)
.Select(g => g.Key)
.ToList();
Note that the LINQ code will not alter your existing groupA and groupB lists. If you just want to distinct them, you could just do
groupA = groupA.Distinct().ToList();
groupB = groupB.Distinct().ToList();
You can easily do it with Linq:
List<int> dups = groupA.Intersect(groupB).ToList();
List<int> distinct = groupA.Except(groupB).ToList();
(assuming I correctly understood what you were trying to do)
The title of the question is "Compare two List", some people who are interested in true/false only result will land to the question
use Enumerable.SequenceEqual Method
if (listA.SequenceEqual(listB))
{
// they are equal
}
You need to find the missing elements in both:
List<int> onlyInA = groupA.Except(groupB).ToList();
List<int> onlyInB = groupB.Except(groupA).ToList();
Or in a single linq:
List<int> missing = groupA.Except(groupB).Union(groupB.Except(groupA)).ToList()
Note - as with all linq, its worth pointing out that its not the most efficient way to do this. All list iterations have a cost. A longer-hand way of sorting both lists then iterating over them together would be quicker if the lists were REALLY large...

Categories

Resources