Editting a 2D array with Linq and ForEach - c#

I'm trying to solve this question in a Linqified way
"Finding diagonal in 2D array and replacing with 0 above it"
This is my Linqfied solution:
Random r = new Random();
//int[,] mas = new int[4, 5];
int[][] mas = new int[4][];
for (int i = 0; i < mas.Length; i++)
{
mas[i] = new int[5];
for (int j = 0; j < mas[i].Length; j++)
{
mas[i][j] = r.Next(1, 10);
Console.Write("{0}\t", mas[i][j]);
}
Console.WriteLine();
}
// my code starts here
for (int i = 0; i < mas.Length; i++)
{
mas[i] = mas[i].Select((c, ind) =>
{
if (ind > i)
c = 0;
return c;
}).ToArray();
}
It works, but I want to get rid of the for-loop, then I did:
mas.ToList().ForEach(m0 =>
{
m0 = m0.Select((c, ind) =>
{
if (ind > k)
c = 0;
return c;
}).ToArray();
k++;
});
Despite the inner elements m0 get the desired values changed to zero in debug time, the mas 2D array don't get its values changed.

You can use linq like so:
mas = mas
.Select((a, i) => a.Select((c, j) => j > i ? 0 : c).ToArray())
.ToArray();

You're changing the parameter to the method, which is a copy of the value in your arrays. Mutating that copy does not mutate the original array.
A proper LINQ-based solution to this problem would be to use LINQ operations such that the results of executing the query is your data set, rather than having the execution of the query mutate some other data set:
var random = new Random();
var query = Enumerable.Range(0, 4)
.Select(i => Enumerable.Range(0, 5)
.Select(j => j > i ? 0 : random.Next(1, 10))
.ToArray())
.ToArray();

Related

finding nth biggest element in multidimensional array

how can I find 5th or 7th, or 15th biggest element in multidimensional array without existing methods (like list.Add)
I will be pleased if you write it in c#
int[,,,] x =new int[100, 20, 35, 200];
...
int indis = 0;
int toplam = 0;
int enss = 0;
for (int i = 0; i < 100; i++) {
for (int j = 0; j < 20; j++) {
toplam = 0;
for (int k = 0; k < 35; k++) {
for (int l = 0; l < 200; l++) {
toplam += x[i, j, k, l];
}
}
if (toplam > enss) {
enss = toplam;
indis = j;
}
}
}
You can try to query with a help of Linq (let .NET use Add if it wants). The crucial thing is to treat multidimensional array T[,,,] as IEnumerable<T>; we can use OfType() for this. Having IEnumerable<T> we can solve the rest. To find max item, just call Max():
using System.Linq;
...
int[,,,] x = new int[100, 20, 35, 200];
...
int max = x.OfType<int>().Max();
If you want to find nth biggest item, you can sort, skip n - 1 items and take the first:
...
int n = 7;
int maxN = x
.OfType<int>()
.OrderByDescending(item => item)
.Skip(n - 1)
.First();
Using linq :
var table = Enumerable.Range(0, x.GetLength(0))
.SelectMany((a, i) => Enumerable.Range(0, x.GetLength(1))
.SelectMany((b, j) => Enumerable.Range(0, x.GetLength(2))
.SelectMany((c, k) => Enumerable.Range(0, x.GetLength(3)
.Select((d, l) => new { x = x[i, j, k, l], i = i, j = j, k = k, l = l }))))
.OrderByDescending(x => x.x);

Tasks fail to launch properly

private static int[] Sort(int[] arr)
{
int temp;
int[] result = new int[arr.Length];
Array.Copy(arr, result, arr.Length);
for (int i = 0; i < result.Length - 1; i++)
{
for (int j = i + 1; j < result.Length; j++)
{
if (result[i] > result[j])
{
temp = result[i];
result[i] = result[j];
result[j] = temp;
}
}
}
return result;
}
public static List<List<T>> ChunkBy<T>(this List<T> source, int chunkSize)
{
return source
.Select((x, i) => new { Index = i, Value = x })
.GroupBy(x => x.Index / chunkSize)
.Select(x => x.Select(v => v.Value).ToList())
.ToList();
}
...
List<List<int>> unsortedLists = list.ChunkBy(chunkSize);
int count = unsortedLists.Count;
List<List<int>> lists = new List<List<int>>();
Task<List<int>>[] tasks = new Task<List<int>>[count];
//from 0 to 9
for (int i = 0; i < count; i++)
{
tasks[i] = new Task<List<int>>(() =>
{
lists[i] = Sort(unsortedLists[i].ToArray()).ToList(); //out of range exception i = 10
return lists[i];
});
}
for(int i = 0; i < count; i++)
{
tasks[i].Start();
}
for(int i = 0; i < count; i++)
{
lists[i] = await tasks[i]; //array out of range
}
I'm facing extremely weird error. I have slightly simplified my code and provided the important part.
I'm creating a bunch of tasks, starting them, and then it fails instantly at 3rd loop. For some reason it uses old iterator that ended on 9, and increases it to 10, creating exception. I have no idea what to do, at the moment it looks like error of the environment.
You should never use the task constructor. In this case, use Task.Run instead.
Your other problem is that the for loop in C# only defines a single loop variable, and lambdas close over variables, not values. This is why you're seeing an i that is 10.
The simplest solution is to do away with the for loops completely:
var tasks = Enumerable.Range(0, count)
.Select(i => Task.Run(() =>
{
lists[i] = Sort(unsortedLists[i].ToArray()).ToList();
return lists[i];
}))
.ToList();
var results = await Task.WhenAll(tasks);

Remove Nulls from string[,]

I have a string array defined in c# as
string[,] options = new string[100,3];
Throughout the code it gets populated with data but not always filled.
So if I have 80 parts of it filled and 20 parts of it not filled. The 20 parts have nulls in them or 60 nulls at the end. Is there an easy way to resize the array so that after filling it the array is the same as
String[,] options = new string[80,3];
It would have to be resized based on the position of the first set of 3 nulls it found.
If this was a jagged array I would have done
options = options.Where(x => x != null).ToArray();
The method is quite long, because it has to check every row twice...
public static string[,] RemoveEmptyRows(string[,] strs)
{
int length1 = strs.GetLength(0);
int length2 = strs.GetLength(1);
// First we count the non-emtpy rows
int nonEmpty = 0;
for (int i = 0; i < length1; i++)
{
for (int j = 0; j < length2; j++)
{
if (strs[i, j] != null)
{
nonEmpty++;
break;
}
}
}
// Then we create an array of the right size
string[,] strs2 = new string[nonEmpty, length2];
for (int i1 = 0, i2 = 0; i2 < nonEmpty; i1++)
{
for (int j = 0; j < length2; j++)
{
if (strs[i1, j] != null)
{
// If the i1 row is not empty, we copy it
for (int k = 0; k < length2; k++)
{
strs2[i2, k] = strs[i1, k];
}
i2++;
break;
}
}
}
return strs2;
}
Use it like:
string[,] options = new string[100, 3];
options[1, 0] = "Foo";
options[3, 1] = "Bar";
options[90, 2] = "fiz";
options = RemoveEmptyRows(options);
As suggested by Alexei, there is another way of doing this:
public static string[,] RemoveEmptyRows2(string[,] strs)
{
int length1 = strs.GetLength(0);
int length2 = strs.GetLength(1);
// First we put somewhere a list of the indexes of the non-emtpy rows
var nonEmpty = new List<int>();
for (int i = 0; i < length1; i++)
{
for (int j = 0; j < length2; j++)
{
if (strs[i, j] != null)
{
nonEmpty.Add(i);
break;
}
}
}
// Then we create an array of the right size
string[,] strs2 = new string[nonEmpty.Count, length2];
// And we copy the rows from strs to strs2, using the nonEmpty
// list of indexes
for (int i1 = 0; i1 < nonEmpty.Count; i1++)
{
int i2 = nonEmpty[i1];
for (int j = 0; j < length2; j++)
{
strs2[i1, j] = strs[i2, j];
}
}
return strs2;
}
This one, in the tradeoff memory vs time, chooses time. It is probably faster, because it doesn't have to check every row twice, but it uses more memory, because it puts somewhere a list of the non-empty indexes.
I went for all rows until you find an row with all null values:
Needs some clean up and will obviously remove non-null rows that occur after the first all null row. The requirement wasn't too clear here
EDIT: Just seen the comment clarifying requirement to remove all null rows - I've tweaked the below to avoid downvotes but a more comprehensive answer is already accepted (and is more efficient) :)
void Main()
{
string[,] options = new string[100,3];
options[0,0] = "bleb";
options[1,1] = "bleb";
options[2,0] = "bleb";
options[2,1] = "bleb";
options[3,2] = "bleb";
options[4,1] = "bleb";
string[,] trimmed = TrimNullRows(options);
Console.WriteLine(trimmed);
}
public string[,] TrimNullRows(string[,] options)
{
IList<string[]> nonNullRows = new List<string[]>();
for (int x = 0; x < options.GetLength(0); x++)
{
bool allNull = true;
var row = new string[options.GetLength(1)];
for (int y = 0; y < options.GetLength(1); y++)
{
row[y] = options[x,y];
allNull &= options[x,y] == null;
}
if (!allNull)
{
nonNullRows.Add(row);
}
}
var optionsTrimmed = new string[nonNullRows.Count, options.GetLength(1)];
for (int i=0;i<nonNullRows.Count;i++)
{
for (int j=0;j<options.GetLength(1);j++)
{
optionsTrimmed[i, j] = nonNullRows[i][j];
}
}
return optionsTrimmed;
}
You can also get yourself some helpers to convert between jagged and multi-dimensional representations. This is pretty silly, of course, but for arrays as small as the ones you're showing (and also, very sparse arrays), it'll be fine.
void Main()
{
string[,] options = new string[100,3];
options[3, 1] = "Hi";
options[5, 0] = "Dan";
var results =
options
.JagIt()
.Where(i => i.Any(j => j != null))
.UnjagIt();
results.Dump();
}
static class Extensions
{
public static IEnumerable<IEnumerable<T>> JagIt<T>(this T[,] array)
{
for (var i = 0; i < array.GetLength(0); i++)
yield return GetRow(array, i);
}
public static IEnumerable<T> GetRow<T>(this T[,] array, int rowIndex)
{
for (var j = 0; j < array.GetLength(1); j++)
yield return array[rowIndex, j];
}
public static T[,] UnjagIt<T>(this IEnumerable<IEnumerable<T>> jagged)
{
var rows = jagged.Count();
if (rows == 0) return new T[0, 0];
var columns = jagged.Max(i => i.Count());
var array = new T[rows, columns];
var row = 0;
var column = 0;
foreach (var r in jagged)
{
column = 0;
foreach (var c in r)
{
array[row, column++] = c;
}
row++;
}
return array;
}
}
The JagIt method is pretty simple of course - we'll just iterate over the rows, and yield the individual items. This gives us an enumerable of enumerables, which we can use in LINQ quite easily. If desired, you could transform those into arrays, of course (say, Select(i => i.ToArray()).ToArray()).
The UnjagIt method is a bit more talkative, because we need to create the target array with the correct dimensions first. And there's no unyield instruction to simplify that :D
This is pretty inefficient, of course, but that isn't necessarily a problem. You could save yourself some of the iterations by keeping the inner enumerable an array, for example - that will save us having to iterate over all the inner items.
I'm mostly keeping this as the memory-cheap, CPU-intensive alternative to #xanatos' memory-intensive, CPU-cheap (relatively).
Of course, the main bonus is that it can be used to treat any multi-dimensional arrays as jagged arrays, and convert them back again. General solutions usually aren't the most efficient :D
Yet another variant with linq
static string[,] RemoveNotNullRow(string[,] o)
{
var rowLen = o.GetLength(1);
var notNullRowIndex = (from oo in o.Cast<string>().Select((x, idx) => new { idx, x })
group oo.x by oo.idx / rowLen into g
where g.Any(f => f != null)
select g.Key).ToArray();
var res = new string[notNullRowIndex.Length, rowLen];
for (int i = 0; i < notNullRowIndex.Length; i++)
{
Array.Copy(o, notNullRowIndex[i] * rowLen, res, i * rowLen, rowLen);
}
return res;
}

How to find a particular string in a two dimensional array of string?

I need to find a string in a two dimensional array and I don't know how. The code should look like this:
...
Random x = new.Random();
Random y = new.Random();
string[,] array = new string[10,10];
{
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
array[i, j] = "";
}
}
}
array[x.Next(0,10),y.Next(0,10)] = "*";
...
The * symbol is always in a different spot and I'd like to know how do I find it. Thanks
You can find it by iterating through the array just like you did for initializing it, except instead of assigning the array index a value, you'll check it for equality:
int i = 0;
int j = 0;
bool found = false;
for (i = 0; i < 10 && !found; i++)
{
for (j = 0; j < 10; j++)
{
if (array[i, j] == "*")
{
found = true;
break;
}
}
}
if (found)
Console.WriteLine("The * is at array[{0},{1}].", i - 1, j);
else
Console.WriteLine("There is no *, you cheater.");
As an alterntive search query with LINQ:
Random xRnd = new Random(DateTime.Now.Millisecond);
Random yRnd = new Random(DateTime.Now.Millisecond);
string[,] array = new string[10, 10];
array[xRnd.Next(0, 10), yRnd.Next(0, 10)] = "*";
var result =
Enumerable.Range(0, array.GetUpperBound(0))
.Select(x => Enumerable.Range(0, array.GetUpperBound(1))
.Where(y => array[x, y] != null)
.Select(y => new { X = x, Y = y }))
.Where(i => i.Any())
.SelectMany(i => i)
.ToList();
result is a list of matches in the form of X,Y

Improving performance of a Nested Loop

This logic is to find the number n in the array in which the range between n and n + 5 will include the most numbers in the array. I came up with a solution but it requires a nested loop, therefore it is kind of slow. Is there any way to improve its performance? Thanks in advance.
The array is guaranteed to sorted.
int[] myArray = new int[]{1,2,4,5,7,9,15,19};
int bestNumber = 0;
int MaxMatchFound = 0;
for (int o = 0; o < myArray.Length; o++)
{
int TempMatchFound = 0;
for (int i = 0; i < myArray.Length; i++)
{
if (myArray[i] >= myArray[o] && myArray[i] <= (myArray[o] + 5))
{
TempMatchFound++;
}
}
if (TempMatchFound > MaxMatchFound)
{
bestNumber = myArray[o];
MaxMatchFound = TempMatchFound;
}
}
return bestNumber;
Bucket the values, then loop over the values v and sum the associated counts for all values w that satisfy v <= w <= v + 5, then find the max count:
var buckets = myArray.GroupBy(n => n)
.ToDictionary(g => g.Key, g => g.Count());
var rangeCounts =
buckets.Keys
.Select(v =>
new {
Value = v,
Count = Enumerable.Range(0, 6)
.Sum(i => buckets.ContainsKey(v + i) ?
buckets[v + i] :
0
)
}
);
var bestRange = rangeCounts.MaxBy(x => x.Count);
Now, bestRange.Value is the starting point for your best range and bestRange.Count is the number of elements falling into the range [bestRange.Value, bestRange.Value + 5]. Here, I've used MaxBy.
Think this gets you linear performance. Building dictionary is linear, building rangeCounts is linear, MaxBy is linear. Even works on non-sorted.
Here you go: This runs in O(N) time, and O(1) memory. This forms the buckets described in other solutions, then discards them as you move through the array. The Queue is used to track which buckets are 'active' in the sense that they can be added to. The Dictionary will never have more than 6 entries in it, neither will the Queue.
int[] myArray = new int[]{1,2,4,5,7,9,15,19};
Dictionary<int, int> counts = new Dictionary<int, int>();
Queue<int> q = new Queue<int>();
int n = 0;
int currentMaxCount = 0;
for(int i = 0; i < myArray.Length; i++)
{
var currentNum = myArray[i];
if(counts.ContainsKey(currentNum))
{
counts[currentNum]++;
}
else
{
counts[currentNum] = 1;
q.Enqueue(currentNum);
}
for(int j = 1; j <= 5; j++)
{
if(counts.ContainsKey(currentNum - j))
counts[currentNum - j]++;
}
if(q.Peek() + 5 < currentNum)
{
if(counts[q.Peek()] > currentMaxCount)
{
currentMaxCount = counts[q.Peek()];
n = q.Peek();
}
counts.Remove(q.Dequeue());
}
}
while(q.Count > 0)
{
if(counts[q.Peek()] > currentMaxCount)
{
currentMaxCount = counts[q.Peek()];
n = q.Peek();
}
counts.Remove(q.Dequeue());
}
Console.WriteLine("There are {0} matches between {1} and {2}", currentMaxCount, n, n + 5);
Here's a solution that is O(n) and uses O(1) extra space regardless of the range.
It does a single pass through the array, always making 2N comparisons. I don't see any way to improve on this algorithm, although there are certainly micro optimizations that could squeeze a little more speed out of the implementation.
private int FindRange(int[] myArray)
{
const int range = 5;
int start = 0;
int maxMatchFound = 0;
int maxIndex = 0;
for (int i = 0; i < myArray.Length; ++i)
{
if (myArray[i] > myArray[start] + range)
{
int matchLength = i - start;
if (matchLength > maxMatchFound)
{
maxMatchFound = matchLength;
maxIndex = start;
}
// move forward until within range
do
{
++start;
} while (myArray[i] > myArray[start] + range);
}
}
// Final check, from myArray[start] to end of array
int len = myArray.Length - start;
if (len > maxMatchFound)
{
maxMatchFound = len;
maxIndex = start;
}
return maxIndex;
The idea here is that if a particular number a[x] falls within the range for a[i] then it will fall within the range for a[i+1], assuming that x > i. (So in your original array, the value at a[3] falls within the range of a[0], so it will also fall within the range of a[1] and a[2]).
So the index i is incremented until the value it references falls out of the range of a[start]. Then, start is incremented until a[i] is in range again. The two indexes move forward through the array in that alternating fashion.
Here's a one-line LINQ option. Not the best in terms of performance (it iterates multiple times). Still worth noting.
var result = myArray
.OrderByDescending(i => myArray.Count(i2 => i2 >= i && i2 <= i + 5))
.First();

Categories

Resources