Sort multiple arrays based on an ID array - c#

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.

Related

Merge 2 Object Array Data Series That Share the Same [0] Index

So let's say that you have 2 series of data. (Both object arrays, in your choice of a serialized JSON string, or the actual objects).
For Instance:
string str1 = #"{""datapoints"":[[""02/28/2019"",146800.0],[""02/27/2019"",147700.0],[""02/26/2019"",153900.0]]}";
Then, you have a second series that is very similar...
string str2 = #"{""datapoints"":[[""02/28/2019"",145600.0],[""02/27/2019"",143600.0],[""02/26/2019"",152200.0]]}";
Note: the object arrays inside are both a length of "2", and both contain the same "date" as the [0] index.
How does one merge the 2 object arrays into 1, to yield the following output...
string str3 = #"{""datapoints"":[[""02/28/2019"",145600.0,145600.0],[""02/27/2019"",143600.0,143600.0],[""02/26/2019"",152200.0,152200.0]]}";
For clarity, I'm interested in using the [0] index once, and merging the [1] indexes together. (the number values)
Extra credit if this can be a loop, or can be done with any number of series.
Using json.net, you can deserialize each JSON sample to an object that contains a datapoints property that is an enumerable of object arrays, then merge them using the LINQ methods GroupBy() and Aggregate().
Say the JSON samples to be merged are in a string [][] jsonSeriesList like so:
string str1 = #"{""datapoints"":[[""02/28/2019"",146800.0],[""02/27/2019"",147700.0],[""02/26/2019"",153900.0]]}";
string str2 = #"{""datapoints"":[[""02/28/2019"",145600.0],[""02/27/2019"",143600.0],[""02/26/2019"",152200.0]]}";
var jsonSeriesList = new[] { str1, str2 }; // Add others as required
Then you can create a combined series as follows:
var merged = jsonSeriesList.Aggregate(
new { datapoints = Enumerable.Empty<object[]>() },
(m, j) => new
{
datapoints = m.datapoints.Concat(JsonConvert.DeserializeAnonymousType(j, m).datapoints)
// Group them by the first array item.
// This will throw an exception if any of the arrays are empty.
.GroupBy(i => i[0])
// And create a combined array consisting of the key (the first item from all the grouped arrays)
// concatenated with all subsequent items in the grouped arrays.
.Select(g => new[] { g.Key }.Concat(g.SelectMany(i => i.Skip(1))).ToArray())
});
var mergedJson = JsonConvert.SerializeObject(merged);
Notes:
I am deserializing to an anonymous type for brevity. You could create an explicit data model if you prefer.
I am assuming the individual datapoint arrays all have at least one item.
There is no attempt to sort the resulting merged JSON by series date. You could do that if necessary.
The solution assumes that you will never have multiple component arrays in the same series with the same first item, e.g. "02/28/2019" repeated twice. If so, they will get merged also.
Sample .Net fiddle here.
Here is a simplified example (simplified as validations might be required, but hope it given you a place to start):
Convert it into dot net object
Then go through each date point - for each date point go through all the series, adding the values.
//container object for the json series
public class Container
{
public List<List<object>> datapoints;
}
//Input series in JSON
string[] inputSeries = new string[]
{
"{\"datapoints\": [[\"02/28/2019\", 146800.0],[\"02/27/2019\", 147700.0],[\"02/26/2019\", 153900.0]]}",
"{\"datapoints\": [[\"02/28/2019\", 145600.0],[\"02/27/2019\", 143600.0],[\"02/26/2019\", 152200.0]]}"
};
//Container for input series in dot net object
List<Container> con = new List<Container>();
foreach (var series in inputSeries)
{
con.Add(JsonConvert.DeserializeObject<Container>(series));
}
// output container
Container output = new Container();
output.datapoints = new List<List<object>>();
// assuming all series have equal number of data points.....might not be so
for (int i = 0; i < con[0].datapoints.Count; i++)
{
output.datapoints.Add(new List<object>());
// inner loop is to go across series for the same datapoint....
for (int j = 0; j < con.Count; j++)
{
// add the date if this is the first series....after that only add the values
// right now the assumption is that the dates are in order and match....validation logic might be required
if (j == 0)
{
output.datapoints[i].Add(con[j].datapoints[i][0]);
output.datapoints[i].Add(con[j].datapoints[i][1]);
}
else
{
output.datapoints[i].Add(con[j].datapoints[i][1]);
}
}
}

Remove elements from Array that are duplicates if compared to elements in another Array

I have the SuperClass Tile extended in many SubClassess such as GroundTile, WallTile, TrapTile etc..
In another Class, lets call it Main, i can use a function that retrieves me all instances of those classes as an array.
Main(){
void MyFunctionThatDoesThings(){
GroundTile[] grounds = FindObjectsOfType<GroundTile>();
WallTile[] walls = FindObjectsOfType<WallTile>();
// ... *same code but for other Classes* ...
Tile[] tiles = FindObjectsOfType<Tile>();
// ... etc
}
}
What I need to do is to filter from tiles array the elements that are not already present in the other arrays.
Is there a way (or a Linq method) to do this other than Looping through the tiles array, checking if the current element is equal to any element in the other arrays, and if not keep it otherwise delete it?
If you're already storing each array in it's own variable as you show above, then you can filter out the other arrays using the Except() extension method (which you get from using System.Linq;).
Then you can just do something like:
GroundTile[] grounds = FindObjectsOfType<GroundTile>();
WallTile[] walls = FindObjectsOfType<WallTile>();
TrapTile[] traps = FindObjectsOfType<TrapTile>();
Tile[] tiles = FindObjectsOfType<Tile>()
.Except(traps)
.Except(walls)
.Except(grounds)
.ToArray();
Tile[] tiles = FindObjectsOfType<Tile>().Except(
Enumerable.Empty<Tile>()
.Concat( grounds )
...
.Concat( walls ) ).ToArray();
This might do as an example if I understood correctly:
List<int> l = new List<int> { 1, 2, 4 };
List<int> s = new List<int> { 1, 2, 3 };
var result = s.Where(x => l.Contains(x));
One way to do it is by using LINQ.
Assuming you have an ID to identify a particular object.
GroundTile[] grounds = FindObjectsOfType<GroundTile>();
WallTile[] walls = FindObjectsOfType<WallTile>();
// ... *same code but for other Classes* ...
Tile[] tiles = FindObjectsOfType<Tile>();
var existingTiles = grounds.Union(walls);
tiles = tiles.Where(t => existingTiles.All(e => e.ID != t.ID)).ToArray();

What means a combined All with Any LINQ query

I have a line of code:
bool true/false = array1.All(a => array2.Any(t => t.field == a.field));
I do not understand this combination of all + any.
Does it mean 'No field of array1 equals any field of array2' then return true ?
Would this not be the same as array1.Except(array2).Any(); ?
UPDATE
Accidently I put a "!" before the .Any() !!!
I think they are really different, it also depends on how you array is structured. If it has only field property or it has other properties as well.
Code inspection
array1.All(a => array2.Any(t => t.field == a.field));
Return true if For each element in array1 there's at least one element in array2 that has the
same value for the field property
array1.Except(array2).Any();
Return True if there's at least one element of array1 not present in
array2
Now given your context, if field is the only property of your structure it produces the same result, but it does not if there's other things going on.
For example
struct Test
{
public string int { get; set; }
public string int { get; set; }
}
//...
var array1 = new Test[]
{
new Test { Field = 0, OtherField = 1 },
new Test { Field = 1, OtherField = 2 }
}
var array2 = new Test[]
{
new Test { Field = 0, OtherField = 1 },
new Test { Field = 2, OtherField = 2 }
}
First case: is it true that for each element of array1 there's at least one element in array2 with same value in field property? False
Second case: is it true that at least one element of array1 is not present in array2? True
That means return true if there is no item in array2 which has the same value in field for all of items in array1.
Simpler version:
For all items in array1, there is no item in array2 with same value of field.
UPDATE:
Now the modified version is much simpler since it says return true if for all items in array1 there is an item in array2 with same value for field.
In summary, your first solution is to check if all the elements in array1 have the same field value in some element in array2, which could be also translated to:
var areContained =!array1.Select(e=>e.field)).Except(array2.Select(d=>d.field)).Any();
Another variant could be using a hash that could help to the efficiency if the superset if too big:
HashSet<int> hashSet = new HashSet<int>(array2.Select(d=>d.field));//Superset
bool contained = array1.Select(e=>e.field)).All(i => hashSet.Contains(i));
In your alternative you are comparing based on object instances of the arrays by using the default equality comparer, so that can produce a completely different result. Take a look the example in this link

ASP.NET add item to array

I have this array defined:
string[] emailAddress = {};
What I am trying to do is add items to this array like so:
emailAddress[] = de.Properties["mail"][0].ToString();
and I get a cannot convert string to array error. How do I add an item to an array?
string[] emailAddress = new string[1]; // initialize it to a length of 1
emailAddress[0] = de.Properties["mail"][0].ToString(); // assign the string to position 1
If you do not know the length at runtime then use a generic List and convert it afterwards.
var emailAddress = new List<string>();
emailAddress.Add(de.Properties["mail"][0].ToString());
var myArray = emailAddress.ToArray(); // create an array from the list
I recommend you read this article on how to work with arrays in c# (or some other tutorial).
https://msdn.microsoft.com/en-us/library/aa288453(v=vs.71).aspx
Based on your comment that it has to be an array and can't be a List, this might be what you need.
Instantiate the array with a specific length. Something like this
string[] emailAddress = new string[emailAddressde.Properties["mail"].Length];
Then you can loop through with something like
for (var i = 0; i < de.Properties["mail"].Length; i++)
emailAddress[i] = de.Properties["mail"][i].ToString();
to populate your emailAddress array.
You have to initialize your array first with a fixed size:
string[] emailAddress = new string[5]; // array with 5 items
and then you can add items like this:
emailAddress[0] = de.Properties["mail"][0].ToString();
But consider if somehow possible using a List<string> which is much more flexible.
You need to reference a location in your array, what you are trying to do is assign your value as the array.
emailAddress[0] = de.Properties["mail"][0].ToString();
If you know how big your array will be then you can init the array to a static size. For example if you know that the array of email will only every be 2 items (index 0 and 1) then you can init the array to that size like this
string[] emailAddress = string[2];
if the items in the array are unknown (how many email addresses) the you should using something else like
List emailAddresses = new List();
So something like this:
List<string> emailAddresses = new List<string>();
emailAddresses.Add("youremail#mail.com");
emailAddresses.ToArray();

Sorting an array related to another array

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.

Categories