I am trying to figure out a way to solve this issue with Linq, does someone have any idea on how to do this? Trying to find this particular use case has proven to be quite challenging so I hope this question hasn't been asked before even though I suspect I just couldn't find it.
public class Test
{
public int a;
public int b;
}
public Test[] testArray;
public enum Choice { A,B, Both = A|B }
public IEnumerable<int> GetEnumerable(Choice choice)
{
//need to use Linq methods to return an enumerable based on choice
}
//e.g testArray = { (1,2) (3,4) (5,6)
//calling GetEnumerable(Choice.A)
// 1,3,5
//calling GetEnumerable(Choice.Both)
// 1,2,3,4,5,6
Everyone focused on the wrong aspects of my question, yes the [Flags] attribute is missing, yes the enum items should be a power of 2 to be used as Flags.
I already marked the correct answer which is to loop over the collection which is what I did before, I just didn't realize I could yield return a IEnumerable so Implemented an enumerator
All the other solutions use Linq but rely too much on instantiating new objects, for a lazy quick approach that is fine but that's not what I wanted.
No Linq is needed, I would maybe use a switch expression (though there is a smattering of Linq in here):
public IEnumerable<int> GetEnumerable(Choice choice)
=> choice switch
{
Choice.A => testArray.Select(a => a.Item1),
Choice.B => testArray.Select(a => a.Item2),
Choice.Both => testArray.SelectMany(a => new[] { a.Item1, a.Item2 }),
_ => throw new ArgumentException("Invalid choice")
};
Theres an inherit problem with your enum, A|B == B, so I changed Both to be it's own case. This solves the problem with one linq query:
public enum Choice { A, B, Both}
public class Test
{
public int A;
public int B;
public Test(int a, int b)
{
A = a;
B = b;
}
}
public class Program
{
public static void Main()
{
var tests = new List<Test>()
{
new Test(1, 2),
new Test(3, 4),
new Test(5, 6)
};
Console.WriteLine(string.Join(", ", GetEnumerable(tests, Choice.A)));
Console.WriteLine(string.Join(", ", GetEnumerable(tests, Choice.B)));
Console.WriteLine(string.Join(", ", GetEnumerable(tests, Choice.Both)));
/*
* Console Output:
* 1, 3, 5
* 2, 4, 6
* 1, 2, 3, 4, 5, 6
*/
}
private static IEnumerable<int> GetEnumerable(IEnumerable<Test> data, Choice choice)
=> data.SelectMany(d => choice switch
{
Choice.A => new List<int> { d.A },
Choice.B => new List<int> { d.B },
Choice.Both => new List<int> { d.A, d.B },
_ => throw new ArgumentException($"No case exists for Choice enum {choice}")
});
}
If you insist on single Linq query, you can try SelectMany where you can return a required collection to be flatten e.g.
public IEnumerable<int> GetEnumerable(Choice choice) => testArray
.SelectMany(item => choice == Choice.Both ? new int[] {item.A, item.B} :
choice == Choice.A ? new int[] {item.A} :
choice == Choice.B ? new int[] {item.B} :
new int[] {});
However, I'd rather implement a simple foreach loop without any Linq:
// Since you use bit combinations, let's do it explicit with [Flags] attribute
[Flags]
public enum Choice {
None = 0, // let have "None" explicit
A = 1,
B = 2,
Both = A|B
}
public IEnumerable<int> GetEnumerable(Choice choice) {
foreach (var item in testArray) {
if (choice.HasFlag(Choice.A))
yield return item.A;
if (choice.HasFlag(Choice.B))
yield return item.B;
}
}
Related
Suppose I have 2 lists: one containing strings, one containing integers, they differ in length. The application I am building will use these lists to generate combinations of vehicle and coverage areas. Strings represent area names and ints represent vehicle ID's.
My goal is to generate a list of all possible unique combinations used for further investigation. One vehicle can service many areas, but one area can't be served by multiple vehicles. Every area must receive service, and every vehicle must be used.
So to conclude the constraints:
Every area is used only once
Every vehicle is used at least once
No area can be left out.
No vehicle can be left out
Here is an example:
public class record = {
public string areaId string{get;set;}
public int vehicleId int {get;set;}
}
List<string> areas = new List<string>{ "A","B","C","D"};
List<int> vehicles = new List<int>{ 1,2};
List<List<record>> uniqueCombinationLists = retrieveUniqueCombinations(areas,vehicles);
I just have no clue how to make the retrieveUniqueCombinations function. Maybe I am just looking wrong or thinking too hard. I am stuck thinking about massive loops and other brute force approaches. An explanation of a better approach would be much appreciated.
The results should resemble something like this, I think this contains all possibilities for this example.
A1;B1;C1;D2
A1;B1;C2;D1
A1;B2;C1;D1
A2;B1;C1;D1
A2;B2;C2;D1
A2;B2;C1;D2
A2;B1;C2;D2
A1;B2;C2;D2
A2;B1;C1;D2
A1;B2;C2;D1
A2;B2;C1;D1
A1;B1;C2;D2
A2;B1;C2;D1
A1;B2;C1;D2
Here's something I threw together that may or may not work. Borrowing heavily from dtb's work on this answer.
Basically, I generate them all, then remove the ones that don't meet the requirements.
List<string> areas = new List<string> { "A", "B", "C", "D" };
List<int> vehicles = new List<int> { 1, 2 };
var result = retrieveUniqueCombinations(areas, vehicles);
result.ToList().ForEach((recordList) => {
recordList.ToList().ForEach((record) =>
Console.Write("{0}{1};", record.areaId, record.vehicleId));
Console.WriteLine();
});
public IEnumerable<IEnumerable<record>> retrieveUniqueCombinations(IEnumerable<string> areas, IEnumerable<int> vehicles)
{
var items = from a in areas
from v in vehicles
select new record { areaId = a, vehicleId = v };
var result = items.GroupBy(i => i.areaId).CartesianProduct().ToList();
result.RemoveAll((records) =>
records.All(record =>
record.vehicleId == records.First().vehicleId));
return result;
}
public class record
{
public string areaId { get; set; }
public int vehicleId { get; set; }
}
static class Extensions
{
public static IEnumerable<IEnumerable<T>> CartesianProduct<T>(
this IEnumerable<IEnumerable<T>> sequences)
{
IEnumerable<IEnumerable<T>> emptyProduct = new[] { Enumerable.Empty<T>() };
return sequences.Aggregate(
emptyProduct,
(accumulator, sequence) =>
from accseq in accumulator
from item in sequence
select accseq.Concat(new[] { item }));
}
}
This produces the following:
A1;B1;C1;D2;
A1;B1;C2;D1;
A1;B1;C2;D2;
A1;B2;C1;D1;
A1;B2;C1;D2;
A1;B2;C2;D1;
A1;B2;C2;D2;
A2;B1;C1;D1;
A2;B1;C1;D2;
A2;B1;C2;D1;
A2;B1;C2;D2;
A2;B2;C1;D1;
A2;B2;C1;D2;
A2;B2;C2;D1;
Note that these are not in the same order as yours, but I'll leave the verification to you. Also, there's likely a better way of doing this (for instance, by putting the logic in the RemoveAll step in the CartesianProduct function), but hey, you get what you pay for ;).
So lets use some helper classes to convert numbers to IEnumerable<int> enumerations in different bases. It may be more efficient to use List<> but since we are trying to use LINQ:
public static IEnumerable<int> LeadingZeros(this IEnumerable<int> digits, int minLength) {
var dc = digits.Count();
if (dc < minLength) {
for (int j1 = 0; j1 < minLength - dc; ++j1)
yield return 0;
}
foreach (var j2 in digits)
yield return j2;
}
public static IEnumerable<int> ToBase(this int num, int numBase) {
IEnumerable<int> ToBaseRev(int n, int nb) {
do {
yield return n % nb;
n /= nb;
} while (n > 0);
}
foreach (var n in ToBaseRev(num, numBase).Reverse())
yield return n;
}
Now we can create an enumeration that lists all the possible answers (and a few extras). I converted the Lists to Arrays for indexing efficiency.
var areas = new List<string> { "A", "B", "C", "D" };
var vehicles = new List<int> { 1, 2 };
var areasArray = areas.ToArray();
var vehiclesArray = vehicles.ToArray();
var numVehicles = vehiclesArray.Length;
var numAreas = areasArray.Length;
var NumberOfCombos = Convert.ToInt32(Math.Pow(numVehicles, numAreas));
var ansMap = Enumerable.Range(0, NumberOfCombos).Select(n => new { n, nd = n.ToBase(numVehicles).LeadingZeros(numAreas)});
Given the enumeration of the possible combinations, we can convert into areas and vehicles and exclude the ones that don't use all vehicles.
var ans = ansMap.Select(nnd => nnd.nd).Select(m => m.Select((d, i) => new { a = areasArray[i], v = vehiclesArray[d] })).Where(avc => avc.Select(av => av.v).Distinct().Count() == numVehicles);
I have ordered list like in example
var someList = new List<int>{1,1,2,3,5,2,1,3,7,1};
I want to select by using LINQ best(highest sum) sequence of 3 numbers.
In this case answer is 3,7,1 or 1,3,7. Is that possible without change order or sorting?
I have an idea how to do this without LINQ, but I just wanna know to do with LINQ
You can use Skip/Zip to end up with triples. For example:
var triples = list.Zip(list.Skip(1).Zip(list.Skip(2), (b, c) => new { b, c }),
(a, bc) => new { a, bc.b, bc.c });
(That may have some errors - I haven't tested it yet.)
You can then order those triples pretty easily:
var orderedTriples = triples.OrderByDescending(t => t.a + t.b + t.c);
If you're using the triples in multiple contexts, you might want to write an extension method to use Tuple<,,> instead:
public static IEnumerable<Tuple<T, T, T>> InTriples<T>(this IEnumerable<T> source)
{
// Or potentially write custom code to do this. It wouldn't be too hard...
return source.Zip(list.Skip(1).Zip(list.Skip(2), (b, c) => new { b, c }),
(a, bc) => Tuple.Create(a, bc.b, bc.c));
}
As for whether LINQ is suitable for this - having the InTriples method generally available means that the rest of the code becomes pretty simple. Using Skip/Zip isn't going to be terribly efficient, but once you've got the code going using that, you can easily rewrite the InTriples method to use an iteerator block instead.
Alternative solution with summing into list directly, without creating triples:
var bestIndex = someList.Zip(someList.Skip(1), (a, b) => a + b)
.Zip(someList.Skip(2), (a, b) => a + b)
.Select((v, i) => new
{
Value = v,
Index = i
})
.OrderByDescending(x => x.Value )
.First()
.Index;
seems to return first highest sequence
using System;
using System.Collections.Generic;
using System.Linq;
namespace take3highestsum
{
class Program
{
static void Main(string[] args)
{
//question sequence
List<int> intlist = new List<int> { 1, 1, 2, 3, 5, 2, 1, 3, 7, 1 };
//display in console stuff not part of answer
foreach (int a in intlist)
{
Console.Write(a + " ");
}
Console.WriteLine();
//begin answer
//check for legit list since we need at least 3 elements
if (intlist.Count < 3) { throw new Exception("List must have more than 3 elements"); }
//stuff we will need
int lastindx = intlist.Count - 1, baseindex = -1;
//begin LINQ
int[] result = intlist.Select(a =>
{
baseindex++;//increment
//return each sequence of three numbers
return new int[]{
intlist[baseindex],//always base index
baseindex + 1 > lastindx ? 0 : intlist[baseindex + 1], //base index + 1 or 0 if out of bounds
baseindex + 2 > lastindx ? 0 : intlist[baseindex + 2] };//base index + 2 or 0 if out of bounds
}).OrderByDescending(b => b.Sum()).First();
//end LINQ
//end answer
//stuff to display proof
foreach (int a in result)
{
Console.Write(a);
}
Console.ReadLine();
}
}
}
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>());
I don't think SequenceEqual is working between the two because the "middle" elements (IEnumerable<int>) aren't using SequenceEqual.
oneThingy.SequenceEqual(twoThingy)
Short of using String.Join on the middle elements, is there a way to get equality?
SequenceEqual tests using Equals; to use SequenceEquals you'll need to implement it yourself. Try using the Zip operator with sequence equals.
// example
var first = Enumerable.Range(1, 10).Select(i => Enumerable.Range(1, i));
var second = Enumerable.Range(1, 10).Select(i => Enumerable.Range(1, i));
bool nestedSequencesEqual =
// test if each sequence index is equal
first.Zip(second, (f, s) => f.SequenceEqual(s))
// ensure all like sequences are equal
.All(b => b);
// returns true
+1 for #BleuM937 answer.
As an another approach you can use the SequenceEqual overloads with equality comparer:
IEnumerable<IEnumerable<int>> one = new IEnumerable<int>[] { new int[] { 1 }, new int[] { 1, 2, 3 } };
IEnumerable<IEnumerable<int>> two = new IEnumerable<int>[] { new int[] { 1 }, new int[] { 1, 2, 3 } };
bool nestedSequencesEqual = one.SequenceEqual(two, new SequencesComparer<int>());
class SequencesComparer<T> : IEqualityComparer<IEnumerable<T>> {
public bool Equals(IEnumerable<T> x, IEnumerable<T> y) {
return x.SequenceEqual(y);
}
public int GetHashCode(IEnumerable<T> obj) {
return obj.GetHashCode();
}
}
The following code works for me...
public class IntList : List<int>, IEquatable<IntList>
{
public bool Equals(IntList other)
{
return this.SequenceEqual(other);
}
}
void Main()
{
List<IntList> list1 = new List<IntList>(2);
List<IntList> list2 = new List<IntList>(2);
var list11 = new IntList() {1, 2, 3};
list1.Add(list11);
var list21 = new IntList() {1, 2, 3};
list2.Add(list21);
var result = list1.SequenceEqual(list2);
Console.WriteLine(result);
}
Reference: http://msdn.microsoft.com/en-us/library/bb348567(v=vs.100).aspx
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