Sorting an array related to another array - c#

I have two arrays, x and y, where y is the value of the tens of every element in x. Now, I want to sort y. But, the order of y will be different of x's. So, I can't tell after sorting which element in y was related to, for instance, x[0].
I want a "double sorting" maybe.

Array.Sort has an overload that accepts two arrays; one for the keys, and one for the items. The items of both are sorted according to the keys array:
int[] keys = { 1, 4, 3, 2, 5 };
string[] items = { "abc", "def", "ghi", "jkl", "mno" };
Array.Sort(keys, items);
foreach (int key in keys) {
Console.WriteLine(key); // 1, 2, 3, 4, 5
}
foreach (string item in items) {
Console.WriteLine(item); // abc, jkl, ghi, def, mno
}
So in your case, it sounds like you want:
Array.Sort(y,x); // or Sort(x,y); - it isn't 100% clear

How about?
var selectedArr = new int[] { 1, 3, 5, 7, 9 };
var unorderArr = new int[] { 9, 7, 5, 3, 1 };
var orderedArr = unorderArr.OrderBy(o => selectedArr.IndexOf(o));

If we have two arrays of complex objects and want to sort them according to one of the two arrays then we can use the next approach:
// We want to sort "people" array by "Name" and
// accordingly to it reorder "countries" array.
Person[] people = new Person[]
{
new Person {Name = "Fill"},
new Person {Name = "Will"},
new Person {Name = "Bill"},
};
Country[] countries = new Country[]
{
new Country {Name = "Canada"},
new Country {Name = "UK"},
new Country {Name = "USA"}
};
// Here we sort "people" array, but together with each "Person"
// in sorted array we store its "index" in unsorted array. Then we
// will use this "index" to reorder items in "countries" array.
var sorted = people
.Select((person, index) => new {person, index})
.OrderBy(x => x.person.Name)
.ToArray();
// Here "people" array is sorted by "Name", and
// "contries" array is reordered accordingly to it.
people = sorted.Select(x => x.person).ToArray();
countries = sorted.Select(x => countries[x.index]).ToArray();
Another approach is to use overload of the method Array.Sort with IComparer. At first we should implement IComparer:
private class PeopleComparer : IComparer<Person>
{
public int Compare(Person x, Person y)
{
return x.Name.CompareTo(y.Name);
}
}
And then we can sort two our arrays:
Array.Sort(people, countries, new PeopleComparer());
Here is complete sample that demonstrates these two approaches.

If y is always the tens value of x, y probably shouldn't exist - you should probably just calculate it's value directly off of x when needed.
In general, sorting parallel arrays is only possible (without hand rolling a sort algorithm) when the sort algorithm takes a custom "swap" function, which you can implement in terms of swapping elements in both arrays simultaneously. std::sort in C++ and qsort in C don't allow this.
Also in the general case, consider a single array where the element is a pair of items, rather than a parallel array for each item. This makes using "standard" algorithms easier.

Related

Linq - Performant selection of specific class properties in list

I'm having a class with several material properties (e.g . temperature). The cross section of a sample is represented by a list of class elements.
I only need material properties in multiple methods at specific positions of the cross section or indices, respectively.
At the moment I'm using a Linq-select to first create an IEnumerable of the needed property.
Than I'm creating a list using the IEnumerable, where I can select the wanted elements by index.
Example (indices is a List with :
var indices = new List<int>() {1, 3, 7, 15, 30, 50};
var Ts = microstructures.Select(x => x.T).ToList();
var list = new List<double>();
for (int i = 0; i < indices.Count; i++)
{
list.Add(Ts[indices[i]]);
}
Is there a more efficient way without creating a list to perform this task?
microstructures has < 100 elements, indices ~ 10 and the properties of the microstructure can be complex classes themselves.
You may filter the T directly using the where overload that expose the object and it's index. msdn
Then replace the Select by a Select many to flattern List<List> to List
public class Toto {
public List<int> T { get; set; }
}
var target = new HashSet<int>{ 1, 3, 7, 15, 30, 50 };
var inputs = Enumerable.Range(0, 10) // generate Data Sample
.Select(x=>new Toto
{
T = Enumerable.Range(100*x,100).ToList()
});
var r = inputs.SelectMany(x => x.T.Where((y,i)=> target.Contains(i)));
live Demo

I want to swap the value from nested list

I am writing a program where I need to swap values from nested list.
Here is what I am looking for example:
Let's say I have a List<object[]> where these list contains two rows as below:
{"id1", "id2", "id3"}, {1,2,3}
Now I want to make it like below:
{"id1", 1}, {"id2", 2}, {"id3", 3}
How can I do that in C#?
Hopefully I have cleared my point.
With 2 input sequences, this can be treated as a "zip" operation:
List<object[]> list = new List<object[]>
{
new object[] {"id1", "id2", "id3" },
new object[] {1,2,3},
};
var rotated = Enumerable.Zip(list[0], list[1],
(x, y) => new object[] { x, y }).ToList();
Note I would advise against using lots of object[] etc here. There's almost always a better way to represent the data.
With an arbitrary number of input sequences, this would need to be done as a "transpose" operation.

Sort multiple arrays based on an ID array

What i've got so far is basically 2 seperately built arrays with contents like these:
Arraygroup #1:
var nickName = ['Noah', 'Liam', 'Jacob', 'Mason', 'William',];
var lastLogin = ['03042015', '02042015', '02042015', '05032015', '16022015'];
var id = [0001, 0002, 0003, 0004, 0005];
Arraygroup #2:
var banStatus= [0, 0, 2, 1, 0];
var status = ['Online', 'Busy', 'Snooze', 'Offline', 'Offline'];
var id = [0003, 0005, 0001, 0004, 0002];
What i want to do is sort them based on the "id" array they both got, ( but sadly they are in the wrong order ).
The result should be something like this:
id[0] == 0001
nickName[0] == Noah
lastLogin[0] == 03042015
isBanned[0] = 2
status[0] = Snooze
Quite another answer:
Define a class which contains all the properties from your arrays:
class MyClass
{
int Id;
string nickName;
...
}
Populate a List with such objects form your arrays. Sort this list.
Create a List with two-element tuples. First element: the Id array, second element: integers 0 .. length of your array.
List<Tuple<int,int>> arraySortHelperList = new List<Tuple<int,int>>();
for(...)
{
int id = ...
arraySortHelperList.Add(new Tuple<int,int>(id, n++));
}
Sort this list based on the Id element.
Now the second elemnt are the sorted indices i.e. the first item in the sorted list will contain in its second element the index of the array slot which comes first.

IEnumerable - exclude using an array

Does anyone know if it's possible to create a new IEnumerable by using an array parameter to exclude values.
For instance, below is an example of how I imagine it would look.
class Item
{
public int id { get; set; }
public string name { get; set; }
}
IEnumerable looks like this:
item1 {id = 1}
item2 {id = 2}
item3 {id = 3}
I want to create a new IEnumerable but exclude the id numbers in the array.
Made up code to suggest idea:
Int32[] arrayList = {1,2};
var newIEnumerable = _exisitingIEnumerable.Where(o => (o.id NOT IN arrayList));
Looking at your question again, when the element type of _exisitingIEnumerable is not the same as that of arrayList, you will need to use Where to filter out the elements of arrayList
_exisitingIEnumerable.Where(o => !arrayList.Contains(o.Id))
Original answer:
_exisitingIEnumerable.Except(arrayList)
will return the distinct elements from _exisitingIEnumerable that are not in arrayList
If you need duplicates, you can use
_exisitingIEnumerable.Where(o => !arrayList.Contains(o))
What's wrong with the approach you suggested in the question? You can use Where and check if the array contains the value. Below the example using List as a target collection:
var myList = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8 };
int[] myArray = { 1, 2, 3 };
var result = new List<int>(myList.Where(n => !myArray.Contains(n)));

c# Linq grouped by with composite key of int, list<int>

I have a product object that has a certain location and allowable shipping methods for that product. What I'm trying to do is Group the products by location AND by the allowable ship methods.
For example the following example data would product two groupings one with IDLocation = 1 and ShipMethods of 1,2 with a count of 2 and the other would be IDLocation = 1 and ShipMethods of 1,2 with a count of 3.
public class CartProduct
{
public int IDLocation { get; set; }
public List<int> ShipMethods { get; set; }
public List<CartProduct> GetExampleData()
{
return new List<CartProduct>() { new CartProduct() { IDLocation = 1, ShipMethods = new List<int>(){ 1, 2 } },
new CartProduct() { IDLocation = 1, ShipMethods = new List<int>(){ 1, 2 } },
new CartProduct() { IDLocation = 1, ShipMethods = new List<int>(){ 3, 4 } },
new CartProduct() { IDLocation = 1, ShipMethods = new List<int>(){ 3, 4 } },
new CartProduct() { IDLocation = 1, ShipMethods = new List<int>(){ 3, 4 } }
};
}
}
I would like to see a grouping of IDLocation first, then if the ship methods are the same group those together as well.
I've tried several version of group by and select many with no luck.
List<CartProduct> CPList = new CartProduct().GetExampleData();
var GroupItems = CPList.GroupBy(x => x.IDLocation) // then by ShipMethods??
I think that the key is to use GroupBy's ability to specify a "key" to use when grouping. You want to group things together that have the same IDLocation and the same set of ShipMethods, so the key should include those things. Ideally you'd use a proper comparer that does the right thing. The hackish way to do this (which is easier to write, so I can tell this'll work) is to mash everything together into a string, so that the normal string comparison does what we want. So here's the quick answer:
var answer = GetExampleData()
.GroupBy(x=>String.Format("{0} {1}",
x.IDLocation,
String.Join(",",x.ShipMethods.OrderBy(y=>y))));
For better performance, you'll have to implement the "proper" way I'm describing. It's a bit of work but it shouldn't be too hard.
edit: I am sorting the ShipMethods so that something that can be shipped via 1 or 2 is correctly seen to be the same thing as if it can be shipped via 2 or 1. Ideally the ShipMethods list is already sorted so we can save the time.
(the funky formatting is to try to make it visible without scrolling)
The comparer argument in GroupBy allows you to define equality for purposes of the object grouping. The comparer is a separate class that compares two like objects and returns true if they are equal. The class, which needs to implement IComparer<CartItem>, can be implemented like this:
class CartGroupComparer : IEqualityComparer<CartProduct>
{
public bool Equals(CartProduct x, CartProduct y)
{
return x.IDLocation == y.IDLocation
&& x.ShipMethods.OrderBy(x=>x)
.SequenceEqual(y.ShipMethods.OrderBy(x=>x));
}
public int GetHashCode(CartProduct obj)
{
return obj.IDLocation.GetHashCode()
^ obj.ShipMethods.Sum().GetHashCode();
}
}
(Note: for simplicity, this assumes that ShipMethods will never be null.)
The Equals method tests two items for equality; if equal, they will be added added to the same group. The GetHashCode method must return an equal value for equal items, and a simple implementation is above.
You can use this comparer directly in your GroupBy clause:
new CartProduct().GetExampleData()
.GroupBy(a => a, new CartGroupComparer());

Categories