Sorting a list of 2D arrays - c#

I'm trying to sort a list containing bool 2D arrays like below.
List<bool[,]>boolList;
bool[,] bool2DArray = {
{true,true,true,true},
{true,true,true,true},
{true,true,true,true}
};
I've been trying to sort the list they're in by the amount of true counts within each 2D Array.
After some research and looking over Stack Overflow I haven't been able to find a solution for this specific issue; many of the solutions I was able to find wouldn't work for me in this situation where I'm not comparing them directly, but rather the result of a calculation on them which led me to trying with a Lambada type solution which also failed but I think that might have been due to me not understanding how to implement it correctly.
Edit
Simple function I made for getting the count
int GetCount(bool[,]bool2DArray) {
int count = 0;
int rows = bool2DArray.GetUpperBound(0);
int columns = bool2DArray.GetUpperBound(1);
for (int x = 0; x <= rows; x++) {
for (int i = 0; i <= columns; i++) {
bool isTrue = bool2DArray[x, i];
if (isTrue) {
count++;
}
}
}
return count;
}
And this is the Lambada type solution I think is in the right direction but isn't valid.
List<bool[,]> sortedList = boolList.Sort((a,b) => (GetCount(a).CompareTo(GetCount(b))));

You'd first want to see how to easily work with the 2d array and count the number of trues in it. To do so for a single item, you could do something similar to what is found in this question: Fast way to convert a two dimensional array to a List ( one dimensional )
bool2DArray.Cast<bool>().Count(i => i)
Then wrapping that with OrderDescendingBy you get the desired result:
var collection = new List<bool[,]> { bool2DArray, ... };
var result = collection.OrderByDescending(item => item.Cast<bool>().Count(i => i));

I prefer this approach which I think is more readable and cover more usecases:
class Program
{
static void Main(string[] args)
{
List<bool[,]> boolList = new List<bool[,]>() {
new bool[,] {
{true,true,true,true},
{true,true,true,true},
{true,true,true,true}
},
new bool[,] {
{false,true,true,true},
{false,true,true,true},
{false,true,true,true}
}
};
boolList = OrderBoolArraysByBoolean(boolList, true);
}
private static List<bool[,]> OrderBoolArraysByBoolean(List<bool[,]> listOfArrays, bool value)
{
return listOfArrays.OrderByDescending(x => x.Cast<bool>().Count(i => i == value)).ToList();
}
}

Related

Is there a way to yield, emit or otherwise materialize a previously empty array in C# / LINQ?

I have an object with a dynamic array of strings which I've implemented as follows:
public class MyThing {
public int NumberOfThings { get; set; }
public string _BaseName { get; set; }
public string[] DynamicStringArray {
get {
List<string> dsa = new List<string>();
for (int i = 1; i <= this.NumberOfThings; i++) {
dsa.Add(string.Format(this._BaseName, i));
}
return dsa.ToArray();
}
}
}
I was trying to be a little cooler earlier and implement something that autocreated the formatted list of arrays in LINQ but I've managed to fail.
As an example of the thing I was trying:
int i = 1;
// create a list with a capacity of NumberOfThings
return new List<string>(this.NumberOfThings)
// create each of the things in the array dynamically
.Select(x => string.Format(this._BaseName, i++))
.ToArray();
It's really not terribly important in this case, and performance-wise it might actually be worse, but I was wondering if there was a cool way to build or emit an array in LINQ extensions.
Will Range help?
return Enumerable
.Range(1, this.NumberOfThings)
.Select(x => string.Format(this._BaseName, x))
.ToArray();
Your property could return an IEnumerable and you could then invoke the ToArray() extension on that, if you needed to.
public string[] DynamicStringArray
{
get
{
for (int i=1; i <= this.NumberOfThings; i++)
yield return string.Format(this._BaseName, i);
}
}
However, yields are inherently slow because of the context switching that goes on. You're better off doing this:
public string[] DynamicStringArray
{
get
{
string[] result = new string[this.NumberOfThings];
for (int i = 0; i < this.NumberOfThings; i++)
{
result[i] = string.Format(this._BaseName, i + 1));
}
return result;
}
}
Those Linq extension methods are nice for when you're feeling lazy. But if you need it to perform well you should avoid them.
I'd rather redesign a bit the current solution:
public class MyThing {
...
// Note IEnumerable<String> instead of String[]
public IEnumerable<String> DynamicString(int numberOfThings) {
if (numberOfThings < 0)
throw new ArgumentOutOfRangeException("numberOfThings");
for (int i = 0; i < numberOfThings; ++i)
yield return string.Format(this._BaseName, i + 1);
}
}
whenever you want, say, an array you can easily obtain it:
MyThing thing = ...;
// Add .ToArray() to have an array
String[] myArray = thing.DynamicString(18).ToArray();
but whenever all you want is just loop there's no need to create an array or list (materialize a result)
// no materialization: no array of 1000000 items
foreach (String item in thing.DynamicString(1000000)) {
...
}

Sort List which I use to fill ComboBox C#

Hello I've got code to fill ComboBox like this:
public ListBox fillComboBox(ListBox cb)
{
cb.Items.Clear();
foreach(string[] s in SO)
{
if (s[1].Split(',')[1].Equals("G5IDD"))
{
cb.Items.Add(s[1].Split(',')[3]);
}
}
cb.Sorted = true;
return cb;
}
In result I've got values sorted like this:
2.1
2.10
2.15
2.2
2.20
But I want it sorted like this
2.1
2.2
2.10
2.15
2.20
SO is ArrayList build by Arrays of string.
Can someone help me sort it way I want?
Thanks in advance
EDIT: Values can be like
4545_3434.2.1/1
4545_3434.2.1/2
4545_3434.2.2
4545_3434.2.2/1
Here is what I would suggest. No need for IComparer. This obviously assumes that the input will always be in the format of [int].[int].
public ListBox fillComboBox(ListBox cb)
{
cb.Items.Clear();
foreach(string[] s in SO.ToArray().OrderBy(s => Int32.Parse(s.ToString().Split('.')[0])).ThenBy(s => Int32.Parse(s.ToString().Split('.')[1])))
{
if (s[1].Split(',')[1].Equals("G5IDD"))
{
cb.Items.Add(s[1].Split(',')[3]);
}
}
return cb;
}
If you want the numbers treated as version, you can use the Version class.
public Version String2Version(string str)
{
string[] parts = str.Split('.');
return new Version(Convert.ToInt32(parts[0]), Convert.ToInt32(parts[1]));
}
public ListBox fillComboBox(ListBox cb)
{
cb.Items.Clear();
foreach(string[] s in SO)
{
if (s[1].Split(',')[1].Equals("G5IDD"))
{
cb.Items.Add( String2Version(s[1].Split(',')[3]));
}
}
cb.Sorted = true;
return cb;
}
You can use custom comparer(IComparer) in your code to achieve it,
I have provide an example.You have to change the logic of
public int Compare(object a, object b)
To achieve your specific requirement
class Program
{
private static ArrayList arl;
public static void Main(string[] args)
{
arl = new ArrayList();
arl.Add("2.1/1");
arl.Add("2.1/2");
arl.Add("2.2");
arl.Add("2.2/1");
arl.Sort(new IDDSort());
foreach (var value in arl)
{
Console.WriteLine(value);
}
Console.Read();
}
}
public class IDDSort : IComparer
{
public int Compare(object x, object y)
{
if (x == y) return 0;
var xparts = x.ToString().Replace("/","").Split('.');
var yparts = y.ToString().Replace("/", "").Split('.');
var length = new[] { xparts.Length, yparts.Length }.Max();
for (var i = 0; i < length; i++)
{
int xint;
int yint;
if (!Int32.TryParse(xparts.ElementAtOrDefault(i), out xint)) xint = 0;
if (!Int32.TryParse(yparts.ElementAtOrDefault(i), out yint)) yint = 0;
if (xint > yint) return 1;
if (yint > xint) return -1;
}
//they're equal value but not equal strings, eg 1 and 1.0
return 0;
}
}
This should work:
Array.Sort(SO, new AlphanumComparatorFast());
(if SO is the array with your version numbers)
Derive from ListBox and override Sort method implementing your own algorithm. For example the one suggested by Feroc.
Check link below:
http://msdn.microsoft.com/pl-pl/library/system.windows.forms.listbox.sort(v=vs.110).aspx
Hello I achieved my goal by coping part of array to list. (I needed only names on list not whole arrays). And used lambda expression for it.
list = list.OrderBy(x => Int32.Parse(x.Split('.')[2].Split('/')[0]))
.ThenBy(x =>
Int32.Parse(x.Split('.')[2].Split('/').Length > 1 ? x.Split('.')[2].Split('/')[1] : x.Split('.')[2].Split('/')[0])
).ToList();
So now I got it sorted like this:
0001.1
0001.2
0001.2/1
0001.2/2
0001.3
0001.3/1
etc.
Thanks everyone for help.

Advanced Remove Array Duplicated

I have 3 arrays.
Array 1 = {1,1,1,1,2,2,3,3}
Array 2 = {a,a,a,a,e,e,b,b}
Array 3 = {z,z,z,z,z,z,z,z}
I would like to remove all duplicates from array 1 and also remove the same element at said duplicate in the other arrays to keep them all properly linked.
I know you can use .Distinct().ToArray() to do this for one array, but then the other arrays would not have the elements removed as well.
The result would look like this.
Array 1 = {1,2,3}
Array 2 = {a,e,b}
Array 3 = {z,z,z}
I'm guessing the only way to solve this would be the following.
For(int a = 0; a < Array1.count; a++) {
For(int b = a + 1; b < Array1.count; b++) {
if(Array1[a]==Array1[b]) {
Array1.RemoveAt(b);
Array2.RemoveAt(b);
Array3.RemoveAt(b);
}
}
}
Would be nice to find a simple predefined function however!
var distinctIndexes = array1
.Select((item, idx) => new { Item = item, Index = idx })
.GroupBy(p => p.Item)
.Select(grp => grp.First().Index);
var result1 = distinctIndexes.Select(i => array1[i]).ToArray();
var result2 = distinctIndexes.Select(i => array2[i]).ToArray();
var result3 = distinctIndexes.Select(i => array3[i]).ToArray();
Note this won't necessarily use the first unique element from the first array. If you need to do that you can calculate the indexes as
var distinctIndexes = array1
.Select((item, idx) => new { Item = item, Index = idx })
.Aggregate(new Dictionary<int, int>(), (dict, i) =>
{
if (! dict.ContainsKey(i.Item))
{
dict[i.Item] = i.Index;
}
return dict;
})
.Values;
You should consider what data structure you're using carefully. Is this "remove" operation likely to happen all at once? How often? (I'm not challenging your use of Array necessarily, just a general tip, but your scenario seems weird). Also, you did not explain if this is an index-based removal or an element based removal. If I was implementing this, I would be tempted to create a new Array and add all remaining elements to the new Array in a loop, ignoring the elements you want to remove. Then simply reassign the reference with '='. Of course, that depends on the maximum expected size of the Array, since a copy like I suggested would take up more memory (usually wouldn't be a problem).
I don't really know of a clean way to do what you're asking, but this is a generic example of doing what you asked?
static void RemoveDupes(ref Array a1, ref Array a2, ref Array a3)
{
Type a1t, a2t, a3t;
int newLength, ni, oi;
int[] indices;
a1t = a1.GetType().GetElementType();
a2t = a1.GetType().GetElementType();
a3t = a1.GetType().GetElementType();
Dictionary<object, List<int>> buckets = new Dictionary<object, List<int>>();
for (int i = 0; i < a1.Length; i++)
{
object val = a1.GetValue(i);
if (buckets.ContainsKey(val))
buckets[val].Add(i);
else
buckets.Add(val, new List<int> { i });
}
indices = buckets.Where(kvp => kvp.Value.Count > 1).SelectMany(kvp => kvp.Value.Skip(1)).OrderBy(i => i).ToArray();
newLength = a1.Length - indices.Length;
Array na1 = Array.CreateInstance(a1t, newLength);
Array na2 = Array.CreateInstance(a2t, newLength);
Array na3 = Array.CreateInstance(a3t, newLength);
oi = 0;
ni = 0;
for (int i = 0; i < indices.Length; i++)
{
while (oi < indices[i])
{
na1.SetValue(a1.GetValue(oi), ni);
na2.SetValue(a2.GetValue(oi), ni);
na3.SetValue(a3.GetValue(oi), ni);
oi++;
ni++;
}
oi++;
}
while (ni < newLength)
{
na1.SetValue(a1.GetValue(oi), ni);
na2.SetValue(a2.GetValue(oi), ni);
na3.SetValue(a3.GetValue(oi), ni);
oi++;
ni++;
}
a1 = na1;
a2 = na2;
a3 = na3;
}

Adding to a range of generic lists in c#

Anyone know of a way to add a value to a range of generic lists in c#?
I'm currently building up a large List<List<int>> and the whole process is taking too long and I'm trying to avoid using foreach loops and nested foreach loops in order to shave some time off.
Lets say I had 600 rows in a generic list. For each of the first 200 rows, I'd like to add a "1". For the next 200, I'd like to add a "2". For the next 200, I'd like to add a "3".
The way I'm doing that now, I have to loop through it 600 times and add each one individually, whereas what I'd like to do is loop through it 3 times and add the entries in bulk.
The code I was hoping for would be something like:
List<List<int>> idList = GetFullList(); //list contains 600 rows
int[] newItems = {1, 3, 5};
int count = 0;
int amountToAmend = 200;
foreach (int i in newItems)
{
//List<int> newID = new List<int>();
//newID.Add(i);
(idList.GetRange(count, amountToAmend)).Add(i);
count += amountToAmend;
}
Obviously this doesn't work, but hopefully you can see the kind of thing I'm going for. In my application I'm currently needing to do tens of thousands of unnecessary loops, when often less than 10 could feasibly do the job if the code exists!
UPDATE: I'm not sure I've explained this well, so just to clarify, here are the results I'm looking for here
If I have a list with 6 rows like so:
[6,7,8]
[5,6,7]
[6,4,8]
[2,4,7]
[5,1,7]
[9,3,5]
i know that I'd like to add a 1 to the first 3 rows and a 2 to the next 3 rows, so they would become:
[6,7,8,1]
[5,6,7,1]
[6,4,8,1]
[2,4,7,2]
[5,1,7,2]
[9,3,5,2]
This is easy to do with foreach loops and is how I currently do it, but because of the sheer volume of data involved, I'm looking for ways to cut the time taken on specific functions. I'm not sure if a way exists tbh, but if anyone knows, then it'll be the good people of Stack Overflow :)
You may use Skip and Take methods from LINQ.
like idList.Skip(0).Take(200) it will give you first 200 items from your list, then you may update these items.
For update you may say:
int increment=2;
list.Select(intVal=> intVal+increment).ToList();
How about this:
foreach (int i in newItems)
{
foreach (var row in idList.Skip(count).Take(amountToAmend))
{
row.Add(i);
}
count += amountToAmend;
}
Or with a for-loop:
foreach (int i in newItems)
{
for (int j = 0; j < amountToAmend; j++)
{
idList[count + j].Add(i);
}
count += amountToAmend;
}
You want to have amountToAmend times each item in newItems ?
Like :
200 times 1
200 times 3
200 times 5
If so, you can try :
int amountToAmend = 200;
List<int> newItems = new List<int>(){ 1, 3, 5 };
<List<int>> idList = new List<List<int>>();
newItems.ForEach(i => idList.Add(new List<int>(Enumerable.Repeat(i, amountToAmend))));
List<List<int>> idList = GetFullList(); //list contains 600 rows
var iterator = idList.Begin();
int[] newItems = {1, 3, 5};
int count = 0;
int amountToAmend = 200;
foreach (var item in newItems)
{
iterator = iterator.AddItem(item);
iterator = iterator.MoveForward(amountToAmend);
}
public struct NestedListIterator<T>
{
public NestedListIterator(List<List<T>> lists, int listIndex, int itemIndex)
{
this.lists = lists;
this.ListIndex = listIndex;
this.ItemIndex = itemIndex;
}
public readonly int ListIndex;
public readonly int ItemIndex;
public readonly List<List<T>> lists;
public NestedListIterator<T> AddItem(T item)
{
var list = lists.ElementAtOrDefault(ListIndex);
if (list == null || list.Count < ItemIndex)
return this;//or throw new Exception(...)
list.Insert(ItemIndex, item);
return new NestedListIterator<T>(this.lists, this.ListIndex, this.ItemIndex + 1);
}
public NestedListIterator<T> MoveForward(List<List<T>> lists, int index)
{
//if (index < 0) throw new Exception(..)
var listIndex = this.ListIndex;
var itemIndex = this.ItemIndex + index;
for (; ; )
{
var list = lists.ElementAtOrDefault(ListIndex);
if (list == null)
return new NestedListIterator<T>(lists, listIndex, itemIndex);//or throw new Exception(...)
if (itemIndex <= list.Count)
return new NestedListIterator<T>(lists, listIndex, itemIndex);
itemIndex -= list.Count;
listIndex++;
}
}
public static int Compare(NestedListIterator<T> left, NestedListIterator<T> right)
{
var cmp = left.ListIndex.CompareTo(right.ListIndex);
if (cmp != 0)
return cmp;
return left.ItemIndex.CompareTo(right.ItemIndex);
}
public static bool operator <(NestedListIterator<T> left, NestedListIterator<T> right)
{
return Compare(left, right) < 0;
}
public static bool operator >(NestedListIterator<T> left, NestedListIterator<T> right)
{
return Compare(left, right) > 0;
}
}
public static class NestedListIteratorExtension
{
public static NestedListIterator<T> Begin<T>(this List<List<T>> lists)
{
return new NestedListIterator<T>(lists, 0, 0);
}
public static NestedListIterator<T> End<T>(this List<List<T>> lists)
{
return new NestedListIterator<T>(lists, lists.Count, 0);
}
}
There is no builtin function, although you cannot avoid looping(explicit or implicit) at all since you want to add a new element to every list.
You could combine List.GetRange with List.ForEach:
var newItems = new[] { 1, 2 };
int numPerGroup = (int)(idList.Count / newItems.Length);
for (int i = 0; i < newItems.Length; i++)
idList.GetRange(i * numPerGroup, numPerGroup)
.ForEach(l => l.Add(newItems[i]));
Note that above is not Linq and would work even in .NET 2.0
This is my old approach which was not what you needed:
You can use Linq and Enumerable.GroupBy to redistribute a flat list into nested lists:
int amountToAmend = 200;
// create sample data with 600 integers
List<int> flattened = Enumerable.Range(1, 600).ToList();
// group these 600 numbers into 3 nested lists with each 200 integers
List<List<int>> unflattened = flattened
.Select((i, index) => new { i, index })
.GroupBy(x => x.index / amountToAmend)
.Select(g => g.Select(x => x.i).ToList())
.ToList();
Here's the demo: http://ideone.com/LlEe2

Looping through x number of arrays

How do I loop through x number of arrays and visit all combinations of all cells in all of the arrays? The problem here is there can be x number of arrays of some items inside. For instance,
List<List<string>> _arrays = GetArrayInformation();
I want to compare all the string inside each array with all the other arrays and the strings inside of each array. Do I use while like
while(i < _arrays.Count)
Thanks for your answer. The answer seems simple but when you think about it is kind of tricky and hard.
Update:
Thanks for your answers. I can do this with a 3 arrays like
for(int i = 0; i < _arrays[0].Count; i++) {
for(int l = 0; l < _arrays[1].Count; l++) {
for(int m = 0; m < _arrays[2].Count; m++) {
string _hello = _arrays[0][i] + "|" + _arrays[1][l] + "|" + _arrays[2][m];
}
}
}
Because I have dynamic number of arrays, it gets tricky.
foreach(var array in _arrays)
{
foreach(var s in array)
{
foreach(var otherArray in _arrays)
{
if(otherArray == array) continue;
if(otherArray.Contains(s)) {} // not sure what you want to do
}
}
}
this will loop through every single string seeing if it is in any other array.... it's the straightforward approach, but not very efficient and will duplicate work.
There is no enough information is here
If you need to find elements that exists in few array You will use something like this:
var multipleWords = _arrays
.SelectMany(items => items.Distinct())
.GroupBy(item => item)
.Select(group => new {Item = group.Key, Count = group.Count()})
.Where(item => item.Count > 1)
.Select(item => item.Item)
.ToArray();
multipleWords will contain each word from the all these arrays that exists in two or more arrays
You could use a recursive search function.
public Search<T>(object o, string comparestring)
{
if(o is List<string>)
{
//Compare to your string
}
else
{
//Call this search function with the type of the object in the list.
//Will iterate through all of your strings recursively.
Type t = o.GetType().GetGenericArguments()[0];
foreach( object osub in (T)o)
Search<t>( ((t)o),comparestring)
}
}

Categories