IEnumerable<Object> Data Specific Ordering - c#

I've an object that is include property ID with values between 101 and 199. How to order it like 199,101,102 ... 198?
In result I want to put last item to first.

The desired ordering makes no sense (some reasoning would be helpful), but this should do the trick:
int maxID = items.Max(x => x.ID); // If you want the Last item instead of the one
// with the greatest ID, you can use
// items.Last().ID instead.
var strangelyOrderedItems = items
.OrderBy(x => x.ID == maxID ? 0 : 1)
.ThenBy(x => x.ID);

Depending whether you are interested in the largest item in the list, or the last item in the list:
internal sealed class Object : IComparable<Object>
{
private readonly int mID;
public int ID { get { return mID; } }
public Object(int pID) { mID = pID; }
public static implicit operator int(Object pObject) { return pObject.mID; }
public static implicit operator Object(int pInt) { return new Object(pInt); }
public int CompareTo(Object pOther) { return mID - pOther.mID; }
public override string ToString() { return string.Format("{0}", mID); }
}
List<Object> myList = new List<Object> { 1, 2, 6, 5, 4, 3 };
// the last item first
List<Object> last = new List<Object> { myList.Last() };
List<Object> lastFirst =
last.Concat(myList.Except(last).OrderBy(x => x)).ToList();
lastFirst.ForEach(Console.Write);
Console.WriteLine();
// outputs: 312456
// or
// the largest item first
List<Object> max = new List<Object> { myList.Max() };
List<Object> maxFirst =
max.Concat(myList.Except(max).OrderBy(x => x)).ToList();
maxFirst.ForEach(Console.Write);
Console.WriteLine();
// outputs: 612345

Edit: missed the part about you wanting the last item first. You could do it like this :
var objectList = new List<DataObject>();
var lastob = objectList.Last();
objectList.Remove(lastob);
var newList = new List<DataObject>();
newList.Add(lastob);
newList.AddRange(objectList.OrderBy(o => o.Id).ToList());
If you are talking about a normal sorting you could use linq's order by method like this :
objectList = objectList.OrderBy(ob => ob.ID).ToList();

In result I want to put last item to first
first sort the list
List<int> values = new List<int>{100, 56, 89..};
var result = values.OrderBy(x=>x);
add an extension method for swaping an elements in the List<T>
static void Swap<T>(this List<T> list, int index1, int index2)
{
T temp = list[index1];
list[index1] = list[index2];
list[index2] = temp;
}
after use it
result .Swap(0, result.Count -1);

You can acheive this using a single Linq statment.
var ordering = testData
.OrderByDescending(t => t.Id)
.Take(1)
.Union(testData.OrderBy(t => t.Id).Take(testData.Count() - 1));
Order it in reverse direction and take the top 1, then order it the "right way round" and take all but the last and union these together. There are quite a few variants of this approach, but the above should work.
This approach should work for arbitrary lists too, without the need to know the max number.

How about
var orderedItems = items.OrderBy(x => x.Id)
var orderedItemsLastFirst =
orderedItems.Reverse().Take(1).Concat(orderedItems.Skip(1));
This will iterate the list several times so perhaps could be more efficient but doesn't use much code.
If more speed is important you could write a specialised IEnumerable extension that would allow you to sort and return without converting to an intermediate IEnumerable.

var myList = new List<MyObject>();
//initialize the list
var ordered = myList.OrderBy(c => c.Id); //or use OrderByDescending if you want reverse order

Related

Sorting the list by two parameters simultaneously C#

I would like to sort lists with structures by certain fields/properties of structures. For example there is a structure:
public struct Some
{
public int index;
public DateTime date;
}
Do I have an opportunity to sort such a structure by two parameters simultaneously using the existing methods? Say, at the top of the list of such structures place those that will have the latest date and the largest index. Is it possible to sort simultaneously taking into account these two parameters?
Yes you can using LINQ
with the OrderBy method
OrderBy and ThenBy are the mothods you need.
Example:
list.OrderBy(x => x.index).ThenBy(x => x.date)
If you add Descending you can also sort it the other way round.
Use custom sort comparison function.
Example 1:
public struct MyItem
{
public int index;
public DateTime date;
}
class Program
{
static void Main(string[] args)
{
var items = new MyItem[]{
new MyItem{index=9, date=DateTime.Now},
new MyItem{index=4, date=DateTime.Now},
new MyItem{index=3, date=DateTime.Now},
new MyItem{index=5, date=DateTime.Now},
new MyItem{index=5, date=DateTime.Now + TimeSpan.FromDays(1)},
new MyItem{index=6, date=DateTime.Now},
};
// sort by index, if equal then sort by date
Array.Sort(items, (x, y) =>
{
if (x.index == y.index)
return x.date.CompareTo(y.date);
return x.index.CompareTo(y.index);
});
foreach (var item in items)
Console.WriteLine($"{item.index} {item.date}");
}
}
Example 2:
var items = new List<MyItem>{
new MyItem{index=9, date=DateTime.Now},
new MyItem{index=4, date=DateTime.Now},
new MyItem{index=3, date=DateTime.Now},
new MyItem{index=5, date=DateTime.Now},
new MyItem{index=5, date=DateTime.Now + TimeSpan.FromDays(1)},
new MyItem{index=6, date=DateTime.Now},
};
// sort by index, if equal then sort by date
items.Sort((x, y) => x.index.CompareTo(y.index) == 0 ? x.date.CompareTo(y.date) : x.index.CompareTo(y.index));
Example3: Linq
var items = new List<MyItem>{
new MyItem{index=9, date=DateTime.Now},
new MyItem{index=4, date=DateTime.Now},
new MyItem{index=3, date=DateTime.Now},
new MyItem{index=5, date=DateTime.Now},
new MyItem{index=5, date=DateTime.Now + TimeSpan.FromDays(1)},
new MyItem{index=6, date=DateTime.Now},
};
// sort by index, if equal then sort by date
var newItems = items.OrderBy(x => x.index).ThenBy(x => x.date);
If you want to Sort in place and you don't want to implement IComparable<Some> or IComparer<Some>:
List<Some> myList = ...
...
myList.Sort((left, right) => {
// latest dates on the top (note - for descending sorting)
int result = -left.date.CompareTo(right.date);
// on tie when left and right have the same date we compare indexes
return result == 0
? -left.index.CompareTo(right.index)
: result;
});
In case you have several fragments where you want to sort list in such a way, you can implement a comparer:
public sealed class SomeComparer : IComparer<Some> {
public int Compare(Some left, Some right) {
// latest dates on the top (note - for descending sorting)
int result = -left.date.CompareTo(right.date);
// on tie when left and right have the same date we compare indexes
return result == 0
? -left.index.CompareTo(right.index)
: result;
}
}
Then whenever you want to sort the list:
myList.Sort(new SomeComparer());
Finally, if it's the only order you want to sort Some items, you can make Sort comparable:
public struct Some : IComparable<Some>
{
public int index;
public DateTime date;
//TODO: implement Equals and GetHashCode
public bool CompareTo(Some other) {
int result = -date.CompareTo(other.date);
return result == 0
? -index.CompareTo(other.index)
: result;
}
}
and you can put
myList.Sort();
You can use LINQ to easily sort items in a list by the values contained in a that list.
using System.Linq;
...
myStructs.OrderBy(s => s.index).ThenBy(s => s.date)
Will put everything in order by index. Items with equal index would be sorted by date.

how to check if the list with the same values exists

In these following code segment::
static void Main(string[] args)
{
List<List<int>> bigList = new List<List<int>> { };
bigList.Add(new List<int> { 1, 2 });
bigList.Add(new List<int> { 2, 3 });
bigList.Add(new List<int> { 3, 4 });
List<int> subList = new List<int> { 1, 2 };
Console.WriteLine(bigList.Contains(subList));
}
the output is:: 'False'.
then what is the method to check this. i mean how will the output become 'True'
If you don't care about duplicate entries in the lists you can use:
bigList.Any(b => new HashSet<int>(b).SetEquals(subList))
If you want both lists to contain exactly the same elements you can use this:
bigList.Any(b => b.OrderBy(x => x).SequenceEqual(subList.OrderBy(x => x)))
If you want both lists to have the same elements in the same order you can use this:
bigList.Any(x => x.SequenceEqual(subList))
If the order doesn't matter you can use Any+All:
bool anyContains = bigList
.Any(l => bigList.Count == l.Count && l.All(i => subList.Contains(i)));
Otherwise you can use Any + SequenceEqual
bool anySequencequals = bigList.Any(l => l.SequenceEqual(subList));
Use the All linq statement
var result = bigList.Where(x => x.All(y => subList.Contains(y)));
You can use SequenceEqual method to check with Any:
bigList.Any(x => x.SequenceEqual(subList))
The reason that your code returns "false" is because you are testing if bigList contains subList. Which it does not! BigList contains a list that looks the same as subList but isn't THE subList.
Try this
bigList.Add(subList);
Complete Code
List<List<int>> bigList = new List<List<int>> { };
List<int> subList = new List<int> { 1, 2 };
bigList.Add(subList); //<<<<<<<<<< Here goes Now bigList contains subList
bigList.Add(new List<int> { 2, 3 });
bigList.Add(new List<int> { 3, 4 });
Console.WriteLine(bigList.Contains(subList));// true
Try using SequenceEqual and Any:
bigList.Any(c => c.SequenceEqual(subList));
Or, if you want to use the other way, with Contains, you'll need to make a custom EqualityComparer:
public class CollectionEqualityComparer<T> : IEqualityComparer<IEnumerable<T>>
{
public Equals(IEnumerable<T> x, IEnumerable<T> y)
{
return x.SequenceEqual(y);
}
public GetHashCode(IEnumerable<T> obj)
{
unchecked
{
return obj.Select(x => x.GetHashCode())
.Aggregate(17, (a, b) => a * 31 * b);
}
}
}
And then just use Contains like this:
bigList.Contains(sublist, new CollectionEqualityComparer<int>());

Fastest way to select distinct values from list based on two properties

I have a this list:
List<myobject> list= new List<myobject>();
list.Add(new myobject{name="n1",recordNumber=1});
list.Add(new myobject{name="n2",recordNumber=2});
list.Add(new myobject{name="n3",recordNumber=3});
list.Add(new myobject{name="n4",recordNumber=3});
I'm looking for the fastest way to select distinct objects based on recordNumber, but if there is more than one object with same recordNumber(here recordNumber=3), I want to select object base on its name.(the name provided by paramater)
thanks
It looks like you are really after something like:
Dictionary<int, List<myobject>> myDataStructure;
That allows you to quickly retrieve by record number. If the List<myobject> with that dictionary key contains more than one entry, you can then use the name to select the correct one.
Note that if your list is not terribly long, an O(n) check that just scans the list checking for the recordNumber and name may be fast enough, in the sense that other things happening in your program could obscure the list lookup cost. Consider that possibility before over-optimizing lookup times.
Here's the LINQ way of doing this:
Func<IEnumerable<myobject>, string, IEnumerable<myobject>> getDistinct =
(ms, n) =>
ms
.ToLookup(x => x.recordNumber)
.Select(xs => xs.Skip(1).Any()
? xs.Where(x => x.name == n).Take(1)
: xs)
.SelectMany(x => x)
.ToArray();
I just tested this with a 1,000,000 randomly created myobject list and it produced the result in 106ms. That should be fast enough for most situations.
Are you looking for
class Program
{
static void Main(string[] args)
{
List<myobject> list = new List<myobject>();
list.Add(new myobject { name = "n1", recordNumber = 1 });
list.Add(new myobject { name = "n2", recordNumber = 2 });
list.Add(new myobject { name = "n3", recordNumber = 3 });
list.Add(new myobject { name = "n4", recordNumber = 3 });
//Generates Row Number on the fly
var withRowNumbers = list
.Select((x, index) => new
{
Name = x.name,
RecordNumber = x.recordNumber,
RowNumber = index + 1
}).ToList();
//Generates Row Number with Partition by clause
var withRowNumbersPartitionBy = withRowNumbers
.OrderBy(x => x.RowNumber)
.GroupBy(x => x.RecordNumber)
.Select(g => new { g, count = g.Count() })
.SelectMany(t => t.g.Select(b => b)
.Zip(Enumerable.Range(1, t.count), (j, i) => new { Rn = i, j.RecordNumber, j.Name}))
.Where(i=>i.Rn == 1)
.ToList();
//print the result
withRowNumbersPartitionBy.ToList().ForEach(i => Console.WriteLine("Name = {0} RecordNumber = {1}", i.Name, i.RecordNumber));
Console.ReadKey();
}
}
class myobject
{
public int recordNumber { get; set; }
public string name { get; set; }
}
Result:
Name = n1 RecordNumber = 1
Name = n2 RecordNumber = 2
Name = n3 RecordNumber = 3
Are you looking for a method to do this?
List<myobject> list= new List<myobject>();
list.Add(new myobject{name="n1",recordNumber=1});
list.Add(new myobject{name="n2",recordNumber=2});
list.Add(new myobject{name="n3",recordNumber=3});
list.Add(new myobject{name="n4",recordNumber=3});
public myobject Find(int recordNumber, string name)
{
var matches = list.Where(l => l.recordNumber == recordNumber);
if (matches.Count() == 1)
return matches.Single();
else return matches.Single(m => m.name == name);
}
This will - of course - break if there are multiple matches, or zero matches. You need to write your own edge cases and error handling!
If the name and recordNumber combination is guaranteed to be unique then you can always use Hashset.
You can then use RecordNumber and Name to generate the HashCode by using a method described here.
class myobject
{
//override GetHashCode
public override int GetHashCode()
{
unchecked // Overflow is fine, just wrap
{
int hash = 17;
// Suitable nullity checks etc, of course :)
hash = hash * 23 + recordNumber.GetHashCode();
hash = hash * 23 + name.GetHashCode();
return hash;
}
}
//override Equals
}

How to sort collection quite specifically by linq

var ids = new int[] { 3, 2, 20, 1 };
var entities = categories.Where(entity => ids.Contains(entity.Id));
I have to sort entities by exactly same like in ids array. How can i do that ?
This should do the trick (written off the top of my head, so may have mistakes)
var ids = new int[] { 3, 2, 20, 1 };
var ordering = ids.Select((id,index) => new {id,index});
var entities =
categories
.Where(entity => ids.Contains(entity.Id))
.AsEnumerable() //line not necessary if 'categories' is a local sequence
.Join(ordering, ent => ent.Id, ord => ord.id, (ent,ord) => new {ent,ord})
.OrderBy(x => x.ord.index)
.Select(x => x.ent)
You could use OrderBy with the index of the Ids in ids.
To get the index of an Id from ids, you could create a map of Id to index. That way you can look up the index in almost constant time, instead of having to call IndexOf and traverse the whole list each time.
Something like this:
var idToIndexMap = ids
.Select((i, v) => new { Index = i, Value = v })
.ToDictionary(
pair => pair.i,
pair => pair.v
);
var sortedEntities = categories
.Where(e => ids.Contains(e.Id))
.ToList() // Isn't necessary if this is Linq-to-Objects instead of entities...
.OrderBy(e => idToIndexMap[e.Id])
;
You may have a go with this:
public class Foo
{
public void Bar()
{
int[] idOrder = new int[] { 3, 2, 20, 1 };
var lookup = idOrder.ToDictionary(i => i,
i => Array.IndexOf(idOrder, i));
foreach(var a in idOrder.OrderBy(i => new ByArrayComparable<int>(lookup, i)))
Console.WriteLine(a);
}
}
public class ByArrayComparable<T> : IComparable<ByArrayComparable<T>> where T : IComparable<T>
{
public readonly IDictionary<T, int> order;
public readonly T element;
public ByArrayComparable(IDictionary<T, int> order, T element)
{
this.order = order;
this.element = element;
}
public int CompareTo(ByArrayComparable<T> other)
{
return this.order[this.element].CompareTo(this.order[other.element]);
}
}
This works for unique elements only, but the lookup efford is constant.

Use LINQ to move item to top of list

Is there a way to move an item of say id=10 as the first item in a list using LINQ?
Item A - id =5
Item B - id = 10
Item C - id =12
Item D - id =1
In this case how can I elegantly move Item C to the top of my List<T> collection?
This is the best I have right now:
var allCountries = repository.GetCountries();
var topitem = allCountries.Single(x => x.id == 592);
var finalList = new List<Country>();
finalList.Add(topitem);
finalList = finalList.Concat(allCountries.Where(x=> x.id != 592)).ToList();
What do you want to order by, other than the known top item? If you don't care, you can do this:
var query = allCountries.OrderBy(x => x.id != 592).ToList();
Basically, "false" comes before "true"...
Admittedly I don't know what this does in LINQ to SQL etc. You may need to stop it from doing the ordering in the database:
var query = allCountries.AsEnumerable()
.OrderBy(x => x.id != 592)
.ToList();
LINQ is strong in querying collections, creating projections over existing queries or generating new queries based on existing collections. It is not meant as a tool to re-order existing collections inline. For that type of operation it's best to use the type at hande.
Assuming you have a type with a similar definition as below
class Item {
public int Id { get; set; }
..
}
Then try the following
List<Item> list = GetTheList();
var index = list.FindIndex(x => x.Id == 12);
var item = list[index];
list[index] = list[0];
list[0] = item;
Linq generallyworks on Enumerables, so it doesn't now that the underlying type is a collection. So for moving the item on top of the list I would suggest using something like (if you need to preserve the order)
var idx = myList.FindIndex(x => x.id == 592);
var item = myList[idx];
myList.RemoveAt(idx);
myList.Insert(0, item);
If your function returns only an IEnumerable, you can use the ToList() method to convert it to a List first
If you don't preserve the order you can simply swap the values at position 0 and position idx
var allCountries = repository.GetCountries();
allCountries.OrderByDescending(o => o.id == 12).ThenBy(o => o.id)
This will insert the object with id=12 at the top of the list and rotate the rest down, preserving the order.
Here is an extension method you might want to use. It moves the element(s) that match the given predicate to the top, preserving order.
public static IEnumerable<T> MoveToTop(IEnumerable<T> list, Func<T, bool> func) {
return list.Where(func)
.Concat(list.Where(item => !func(item)));
}
In terms of complexity, I think it would make two passes on the collection, making it O(n), like the Insert/Remove version, but better than Jon Skeet's OrderBy suggestion.
You can "group by" in two groups with Boolean key, and then sort them
var finalList= allCountries
.GroupBy(x => x.id != 592)
.OrderBy(g => g.Key)
.SelectMany(g => g.OrderBy(x=> x.id ));
I know this a old question but I did it like this
class Program
{
static void Main(string[] args)
{
var numbers = new int[] { 5, 10, 12, 1 };
var ordered = numbers.OrderBy(num => num != 10 ? num : -1);
foreach (var num in ordered)
{
Console.WriteLine("number is {0}", num);
}
Console.ReadLine();
}
}
this prints:
number is 10
number is 1
number is 5
number is 12
public static IEnumerable<T> ServeFirst<T>(this IEnumerable<T> source,
Predicate<T> p)
{
var list = new List<T>();
foreach (var s in source)
{
if (p(s))
yield return s;
else
list.Add(s);
}
foreach (var s in list)
yield return s;
}
Its interesting the number of approaches you find when trying to solve a problem.
var service = AutogateProcessorService.GetInstance();
var allConfigs = service.GetAll();
allConfigs = allConfigs.OrderBy(c => c.ThreadDescription).ToList();
var systemQueue = allConfigs.First(c => c.AcquirerId == 0);
allConfigs.Remove(systemQueue);
allConfigs.Insert(0, systemQueue);
To also check if the item was found without Exception, something like:
var allCountries = repository.GetCountries();
var lookup = allCountries.ToLookup(x => x.id == 592);
var finalList = lookup[true].Concat(lookup[false]).ToList();
if ( lookup[true].Count() != 1 ) YouAreInTrouble();
Even easier if you have the object:
listOfObjects.Remove(object);
listOfObjects.Insert(0, object);
I wrote a static extension method to do this. Note this doesn't preserve the order, it simply swaps the item out. If you needed to preserve the order you should do a rotate not a simple swap.
/// <summary>
/// Moves the item to the front of the list if it exists, if it does not it returns false
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="collection"></param>
/// <param name="predicate"></param>
/// <returns></returns>
public static bool MoveToFrontOfListWhere<T>(this List<T> collection, Func<T, bool> predicate)
{
if (collection == null || collection.Count <= 0) return false;
int index = -1;
for (int i = 0; i < collection.Count; i++)
{
T element = collection.ElementAt(i);
if (!predicate(element)) continue;
index = i;
break;
}
if (index == -1) return false;
T item = collection[index];
collection[index] = collection[0];
collection[0] = item;
return true;
}

Categories

Resources