Sorting multiple arrays c# - c#

currently I'm working on a project where i'm required to sort a total of 6 arrays.
I've managed to sort the arrays individually using a quicksort, however, I'm just wondering if there is a way to sort 1 array and reflect that on the order of elements in the other arrays. For example, if I sort my date array to an ascending order, I want the other arrays to still match up with the dates in respect to the new order.
If possible, could this still be done through a quick sort?

I think the more appropriate option will be to create a new class with all different 6 kind of properties
public class myClass
{
public DateTime date{get;set;}
public string name{get;set;}
//....
}
Then create a single array/list of that class.
public List<myClass> arrData;
Now you can sort that array based on any of your desired property and it will keep the order as per your requirements
arrData.OrderBy(x => x.name)
You can replace x.name with any of your myClass property.
This approach makes your code clean and easy to manage as well.

Likely you have a setup something like this:
DateTime [] dates;
string [] names;
int [] ids;
//...etc
Consider instead to condense your data into a single object, then have a single array of that object:
public class MyObject
{
DateTime date { get; set; }
string name { get; set; }
int id { get; set; }
}
Now you will only have 1 array:
MyObject [] objects;
And you can sort the whole collection of objects by their date:
objects.Sort((a, b) => a.date.CompareTo(b.date));
Also consider using Lists rather than straight arrays, as the use case for using vanilla arrays in c# is very small:
List<MyObject> objects;

It looks like LINQ uses the quick sort algorithm for the OrderBy method (see previous StackOverflow question).
Something like this should take care of it for you:
DateTime[] datesOfBirth = new DateTime[] { new DateTime(1955, 10, 28), new DateTime(1955, 2, 24) };
String[] firstNames = new String[] { "William", "Steve" };
String[] lastNames = new String[] { "Gates", "Jobs" };
var people =
datesOfBirth
.Select((_, i) => new
{
DateOfBirth = datesOfBirth[i],
FirstName = firstNames[i],
LastName = lastNames[i]
})
.OrderBy(x => x.DateOfBirth)
.ToArray();

Related

how to use string.join to join all values from an object array?

I have the same situation as this user how to use string.join to join value from an object array? in this question. However, I want to join all values from the object instead of only 1 value.
To recap my question:
I have an array of object e.g:
MyObject[] objs;
and within MyObject it contains 3 string property,
object[0].stringValue1
object[0].stringValue2
object[0].stringValue3
If I want to join the whole array of objects by all of their stringValues (stringValues1,2 and 3), how can I do it?
I think selector doesn’t allow me to select several elements, then how to use string.join to join several values from an object array?
See below for example usage of the two extension methods provided in the implementation section below. The first allows you to select the properties to include, and the second includes all string properties of the object in the source collection which I believe is the exact answer to your question.
Example Usage
Note, the resulting output from the two examples below are ordered differently as a result of how each implementation works, however the results are otherwise identical as a result of the first example specifying all string properties on the MyObj sample type
Live Fiddle Example
// Test Object
public class MyObj
{
public MyObj(string prop1, string prop2, string prop3)
{
Prop1 = prop1;
Prop2 = prop2;
Prop3 = prop3;
}
public string Prop1 { get; set; }
public string Prop2 { get; set; }
public string Prop3 { get; set; }
}
// Sample Data
var list = new List<MyObj> {
new MyObj("A1", "B1", "C1"),
new MyObj("A1", "B2", "C2"),
new MyObj("A3", "B3", "C3")
};
Samples using above object and data
// Example #1 - All properties separated by single separator
Console.WriteLine(list.Join(colSeparator: ','));
// RESULT: A1,A1,A3,B1,B2,B3,C1,C2,C3
// Example #2 - Object property separator, and different object separator
Console.WriteLine(list.Join(colSeparator: ',', rowSeparator: '\n'));
// RESULT: A1,B1,C1
A1,B2,C2
A3,B3,C3
Implementation
public static class EnumerableStringJoinExtension
{
public static string Join<T>(this IEnumerable<T> values, char colSeparator, char? rowSeparator = null)
{
var strProperties = typeof(T).GetProperties().Where(r=>r.PropertyType == typeof(string));
var sb = new StringBuilder();
foreach(var val in values)
sb.Append(string.Join(colSeparator, strProperties.Select(r=> r.GetValue(val)))).Append(rowSeparator ?? colSeparator);
sb.Remove(sb.Length - 1, 1);
return sb.ToString();
}
}
A possible way to solve it is to first create an array of each object's properties (using .Select()), and then flatten the resulting property arrays by using .SelectMany(). Both of those methods are found in the System.Linq namespace. The resulting IEnumerable<string> object's items can then be joined using string.Join().
If MyObject is defined as
class MyObject
{
public string First { get; set; }
public string Second { get; set; }
public string Third { get; set; }
}
and you define objects as
List<MyObject> objects = new()
{
new() { First = "ABC", Second = "DEF", Third = "GHI" },
new() { First = "JKL", Second = "MNO", Third = "PQR" },
new() { First = "STU", Second = "VWX", Third = "YZ" },
};
, a possible implementation is:
var flattenedObjects = objects
.Select(obj => new[] { obj.First, obj.Second, obj.Third })
.SelectMany(properties => properties);
var objectString = string.Join("_", flattenedObjects);
Printing the value of objectString to the console gives:
ABC_DEF_GHI_JKL_MNO_PQR_STU_VWX_YZ
Example fiddle here.
If you just want a one-line, handy C# without hard-coding property name you could try this:
string.Join("\n", objs.Select(o => string.Join("_", o.GetType().GetProperties().Select(p => p.GetValue(o)))));
This produces the following result:
"A_B_C\nD_E_F\nG_H_I"
with the object array as:
var objs = new MyObject[]
{
new MyObject("A", "B", "C"),
new MyObject("D", "E", "F"),
new MyObject("G", "H", "I")
};
But please note that if your class has properties in other types then you might need a Where() between GetProperties() and the second Select() to exclude unwanted properties.

How to add Terms into Terms Query (ElasticSearch NEST C#)?

I'm writing a TermsQuery in NEST. I have created a QueryContainer for it. Here is my code:
QueryContainer qc1 = new TermsQuery()
{
Field = "recordID",
Terms =
};
I want to add int array as Terms and it shows that terms only accept IEnumerable<object>. I have tried converting array to Enumbrable and it's not working.
I just want to know what kind of object Terms accept?
Thanks in advance.
Terms accepts an IEnumerable<object> so it can accept a collection of any objects :) The type to use will depend on the field that you are querying against.
Given the model
public class Document
{
public int Property1 { get; set; }
}
To pass it a collection of int or any other type
var termQuery = new TermsQuery
{
Field = Infer.Field<Document>(d => d.Property1),
Terms = new object[] { 1, 3, 5 }
};

LINQ select one field from list of DTO objects to array

I have DTO class that defines order line like this:
public class Line
{
public string Sku { get; set; }
public int Qty { get; set; }
}
A list of type Line is populated like so:
List<Line> myLines = new List<Line>();
myLines.Add(new Line() { Sku = "ABCD1", Qty = 1 });
myLines.Add(new Line() { Sku = "ABCD2", Qty = 1 });
myLines.Add(new Line() { Sku = "ABCD3", Qty = 1 });
What I want is to use LINQ to get an array of SKUs from the myLines List. How can I go about doing that?
I am currently doing it manually like this ...
// Get SKU List
List<string> mySKUs = new List<string>();
foreach (Line myLine in myLines)
mySKUs.Add(myLine.Sku);
string[] mySKUsArray = mySKUs.ToArray();
I was trying to google for a solution, but I wasn't sure how to word the question...
P.S. is there any benefit/performance gain in using LINQ method to achieve what I am currently doing with foreach?
You can use:
var mySKUs = myLines.Select(l => l.Sku).ToList();
The Select method, in this case, performs a mapping from IEnumerable<Line> to IEnumerable<string> (the SKU), then ToList() converts it to a List<string>.
Note that this requires using System.Linq; to be at the top of your .cs file.
This is very simple in LinQ... You can use the select statement to get an Enumerable of properties of the objects.
var mySkus = myLines.Select(x => x.Sku);
Or if you want it as an Array just do...
var mySkus = myLines.Select(x => x.Sku).ToArray();
I think you're looking for;
string[] skus = myLines.Select(x => x.Sku).ToArray();
However, if you're going to iterate over the sku's in subsequent code I recommend not using the ToArray() bit as it forces the queries execution prematurely and makes the applications performance worse. Instead you can just do;
var skus = myLines.Select(x => x.Sku); // produce IEnumerable<string>
foreach (string sku in skus) // forces execution of the query
You can select all Sku elements of your myLines list and then convert the result to an array.
string[] mySKUsArray = myLines.Select(x=>x.Sku).ToArray();
In the case you're interested in extremely minor, almost immeasurable performance increases, add a constructor to your Line class, giving you such:
public class Line
{
public Line(string sku, int qty)
{
this.Sku = sku;
this.Qty = qty;
}
public string Sku { get; set; }
public int Qty { get; set; }
}
Then create a specialized collection class based on List<Line> with one new method, Add:
public class LineList : List<Line>
{
public void Add(string sku, int qty)
{
this.Add(new Line(sku, qty));
}
}
Then the code which populates your list gets a bit less verbose by using a collection initializer:
LineList myLines = new LineList
{
{ "ABCD1", 1 },
{ "ABCD2", 1 },
{ "ABCD3", 1 }
};
And, of course, as the other answers state, it's trivial to extract the SKUs into a string array with LINQ:
string[] mySKUsArray = myLines.Select(myLine => myLine.Sku).ToArray();

C# Finding element in List of String Arrays

I cannot solve a problem for several hours now.
Here is a simplified scenario.
Let's say there is a list of people with their bids. I'm trying to find a person with the highest bid and return the name. I am able to find the highest bid, but how to I output the name?
List<String[]> list = new List<String[]>();
String[] Bob = { "Alice", "19.15" };
String[] Alice = {"Bob", "28.20"};
String[] Michael = { "Michael", "25.12" };
list.Add(Bob);
list.Add(Alice);
list.Add(Michael);
String result = list.Max(s => Double.Parse(s.ElementAt(1))).ToString();
System.Console.WriteLine(result);
As a result I get 28.20, which is correct, but I need to display "Bob" instead. There were so many combinations with list.Select(), but no success. Anyone please?
The best solution from an architectural point of view is to create a separate class (e.g. Person) that contains two properties Name and Bid of each person and a class Persons that contains the list of persons.
Then you can easily use a LINQ command.
Also instead of storing bids as string, think if bids as floating point or decimal values would be better (or store it in cents and use an int).
I don't have a compiler by hand so it's a bit out of my head:
public class Person
{
public string Name { get; set; }
public float Bid { get; set; }
public Person(string name, float bid)
{
Debug.AssertTrue(bid > 0.0);
Name = name;
Bid = bid;
}
}
public class Persons : List<Person>
{
public void Fill()
{
Add(new Person("Bob", 19.15));
Add(new Person("Alice" , 28.20));
Add(new Person("Michael", 25.12));
}
}
In your class:
var persons = new Persons();
persons.Fill();
var nameOfHighestBidder = persons.MaxBy(item => item.Bid).Name;
Console.WriteLine(nameOfHighestBidder);
This works in the simple example. Not sure about the real one
var result = list.OrderByDescending(s => Double.Parse(s.ElementAt(1))).First();
You can use Jon Skeet's MaxBy.
For usage you can see this question
e.g. in this case
list.MaxBy(s => Double.Parse(s.ElementAt(1)))[0]
More here
Should work:
var max = list.Max(t => double.Parse(t[1]));
list.First(s => double.Parse(s[1]) == max)[0]; // If list is not empty
After finding result just do as below:
list.First(x=>x[1] == result)[0]

Sort a List by a property and then by another

I have an example class containing two data points:
public enum Sort { First, Second, Third, Fourth }
public class MyClass
{
public MyClass(Sort sort, string name) {
this.Sort = sort;
this.Name = name;
}
public Sort Sort { get; set; }
public string Name { get; set; }
}
I'm looking to sort them into groups by their Sort property, and then alphabetize those groups.
List<MyClass> list = new List<MyClass>() {
new MyClass(MyClass.Sort.Third, "B"),
new MyClass(MyClass.Sort.First, "D"),
new MyClass(MyClass.Sort.First, "A"),
new MyClass(MyClass.Sort.Fourth, "C"),
new MyClass(MyClass.Sort.First, "AB"),
new MyClass(MyClass.Sort.Second, "Z"),
};
The output would then be:
(showing Name)
A
AB
D
Z
B
C
I've been able to do this by using a foreach to separate the items into many smaller arrays (grouped by the enum value) but this seems very tedious - and I think there must be some LINQ solution that I don't know about.
Using extension methods, first OrderBy the enum, ThenBy name.
var sorted = list.OrderBy( m => m.Sort ).ThenBy( m => m.Name );
Aside from the nice LINQ solutions, you can also do this with a compare method like you mentioned. Make MyClass implement the IComparable interface, with a CompareTo method like:
public int CompareTo(object obj)
{
MyClass other = (MyClass)obj;
int sort = this.srt.CompareTo(other.srt);
return (sort == 0) ? this.Name.CompareTo(other.Name) : sort;
}
The above method will order your objects first by the enum, and if the enum values are equal, it compares the name. Then, just call list.Sort() and it will output the correct order.
This should do it, I think
var result = from m in list
orderby m.Sort, m.Name
select m;

Categories