linq ordered subset of another list - c#

There are lots and lots of questions on SO about finding if one list is the subset of another list.
i.e. bool isSubset = !t2.Except(t1).Any();
I can't seem to find one that accounts for order
as in given a sequence:
1,1,2,5,8,1,9,1,2
The subsequences...
2,5,8,1,9 true
1,2,5,8,1 true
5,2,1 false
1,2,5,1,8 false
1,1,2 true
1,1,1,2 false

A list in which the order is significant is a generalisation of the concept of string. Therefore you want to use a substring-finding algorithm.
There are several possibilities, but Knuth–Morris–Pratt is a good choice. It has some initial Θ(m) overhead where m is the length of the sublist sought, and then finds in Θ(n) where n is the distance to the sublist sought, or the length of the whole list if it isn't there. This beats the simple item-by-item compare which is Θ((n-m+1) m):
public static class ListSearching
{
public static bool Contains<T>(this IList<T> haystack, IList<T> needle)
{
return Contains(haystack, needle, null);
}
public static bool Contains<T>(this IList<T> haystack, IList<T> needle, IEqualityComparer<T> cmp)
{
return haystack.IndexOf(needle, cmp) != -1;
}
public static int IndexOf<T>(this IList<T> haystack, IList<T> needle)
{
return IndexOf(haystack, needle, null);
}
public static int IndexOf<T>(this IList<T> haystack, IList<T> needle, IEqualityComparer<T> cmp)
{
if(haystack == null || needle == null)
throw new ArgumentNullException();
int needleCount = needle.Count;
if(needleCount == 0)
return 0;//empty lists are everywhere!
if(cmp == null)
cmp = EqualityComparer<T>.Default;
int count = haystack.Count;
if(needleCount == 1)//can't beat just spinning through for it
{
T item = needle[0];
for(int idx = 0; idx != count; ++idx)
if(cmp.Equals(haystack[idx], item))
return idx;
return -1;
}
int m = 0;
int i = 0;
int[] table = KMPTable(needle, cmp);
while(m + i < count)
{
if(cmp.Equals(needle[i], haystack[m + i]))
{
if(i == needleCount - 1)
return m == needleCount ? -1 : m;//match -1 = failure to find conventional in .NET
++i;
}
else
{
m = m + i - table[i];
i = table[i] > -1 ? table[i] : 0;
}
}
return -1;
}
private static int[] KMPTable<T>(IList<T> sought, IEqualityComparer<T> cmp)
{
int[] table = new int[sought.Count];
int pos = 2;
int cnd = 0;
table[0] = -1;
table[1] = 0;
while(pos < table.Length)
if(cmp.Equals(sought[pos - 1], sought[cnd]))
table[pos++] = ++cnd;
else if(cnd > 0)
cnd = table[cnd];
else
table[pos++] = 0;
return table;
}
}
Testing this:
var list = new[]{ 1, 1, 2, 5, 8, 1, 9, 1, 2 };
Console.WriteLine(list.Contains(new[]{2,5,8,1,9})); // True
Console.WriteLine(list.Contains(new[]{1,2,5,8,1})); // True
Console.WriteLine(list.Contains(new[]{5,2,1})); // False
Console.WriteLine(list.Contains(new[]{1,2,5,1,8})); // False
Console.WriteLine(list.Contains(new[]{1,1,2})); // True
Console.WriteLine(list.Contains(new[]{1,1,1,2})); // False

Unfortunately there is no such function in .net. You need Knuth–Morris–Pratt algo for it. One guy already implemented it as linq extension https://code.google.com/p/linq-extensions/

This works for me:
var source = new [] { 1,1,2,5,8,1,9,1,2 };
Func<int[], int[], bool> contains =
(xs, ys) =>
Enumerable
.Range(0, xs.Length)
.Where(n => xs.Skip(n).Take(ys.Length).SequenceEqual(ys))
.Any();
Console.WriteLine(contains(source, new [] { 2,5,8,1,9 })); // true
Console.WriteLine(contains(source, new [] { 1,2,5,8,1 })); // true
Console.WriteLine(contains(source, new [] { 5,2,1 })); // false
Console.WriteLine(contains(source, new [] { 1,2,5,1,8 })); // false
Console.WriteLine(contains(source, new [] { 1,1,2 })); // true
Console.WriteLine(contains(source, new [] { 1,1,1,2 })); // false

there is a workaround to the limitation. You can change the enumerable to a string and then make use of the Contains method.
var t1 = new List<int> {1, 1, 2, 5, 8, 1, 9, 1, 2};
var t2 = new List<int> {2,5,8,1,9};
var t3 = new List<int> {5,2,1};
var t1Str = String.Join(",", t1);
t1Str.Contains(String.Join(",", t2););//true
t1Str.Contains(String.Join(",", t3););//false

You can build your own extension, I wrote a simple IsSubset method:
Console App for testing:
class Program
{
static void Main(string[] args)
{
var list = new List<int> { 1, 3, 5, 2, 4, 6 };
var subList = new List<int> { 3, 5};
var subList2 = new List<int> { 1, 4 };
bool isSublist1 = subList.IsSubset(list);
bool isSublist2 = subList2.IsSubset(list);
Console.WriteLine(isSublist1 + "; " + isSublist2);
/* True; False */
Console.ReadKey();
}
}
IEnumerable Extension:
public static class IEnumerableExtensions
{
public static bool IsSubset<T>(this IEnumerable<T> subsetEnumerable, IEnumerable<T> enumerable)
{
var found = false;
var list = enumerable as IList<T> ?? enumerable.ToList();
var listCount = list.Count();
var subsetList = subsetEnumerable as IList<T> ?? subsetEnumerable.ToList();
var posListCount = subsetList.Count();
/* If the SubList is bigger, it can't be a sublist */
if (listCount < posListCount) {
return false;
}
/* find all indexes of the first item of the sublist in the list */
var firstElement = subsetList.First();
var indexes = new List<int>();
var index = 0;
foreach (var elem in list)
{
if (elem.Equals(firstElement))
{
indexes.Add(index);
}
index++;
}
/* check all first item founds for the subsequence */
foreach (var i in indexes)
{
int x=0;
for (x = 0; x < posListCount && (i + x) < listCount; x++)
{
if (!Equals(subsetList[x], list[(i + x)]))
{
found = false;
break;
}
found = true;
}
if (x + 1 < posListCount)
found = false;
}
return found;
}
}

May be using join can get you what you want. Join will return the matching records. If record count is greater than 0 than there is a match else no match.
Below I have explained through a sample code:
class Program
{
static void Main(string[] args)
{
List<Employee> empList = new List<Employee>
{
new Employee{EmpID = 1},
new Employee{EmpID = 1},
new Employee{EmpID = 2},
new Employee{EmpID = 5},
new Employee{EmpID = 8},
new Employee{EmpID = 1},
new Employee{EmpID = 9},
new Employee{EmpID = 1},
new Employee{EmpID = 2}
};
List<Manager> mgrList = new List<Manager>
{
new Manager{ManagerID = 7},
new Manager{ManagerID = 3},
new Manager{ManagerID = 6}
};
var result = (from emp in empList
join mgr in mgrList on emp.EmpID equals mgr.ManagerID
select new { emp.EmpID}).Count();
Console.WriteLine(result);
Console.ReadKey();
}
}
public class Employee
{
public int EmpID { get; set; }
}
public class Manager
{
public int ManagerID { get; set; }
}

Related

How to concat multiple list of object in single column c#

I'm facing an issue while displaying multiple lists the value in a single row column.
Here is an example of code.
public class Program
{
static void Main(string[] args)
{
Dictionary<string, List<object>> keyvalues = new Dictionary<string, List<object>>();
keyvalues.Add("Code", new List<object>() { 1, 2, 3, 4 });
keyvalues.Add("Name", new List<object>() { "A", "B", "C", "D" });
keyvalues.Add("Age", new List<object>() { 20, 30, 40, 50 });
var listData = keyvalues.Select(x => x.Value).Select((x, i) => new { obj = x, index = i });
var listData = keyvalues.Select((x, iparent) => x.Value.Select((z, i) => new { value = string.Concat(z, x.Value[i]) }).ToList()).ToList();
Console.ReadLine();
}
}
Expected output
1A20
2B30
3C40
4D50
If you are using .Net 6, you could make use of the new 3 way Zip extension.
var result = keyvalues["Code"].Zip(keyvalues["Name"], keyvalues["Age"])
.Select(x=> $"{x.First}{x.Second}{x.Third}");
Why make it so complicated?
for(int x = 0; x<keyValues["Code"].Count; x++)
Console.WriteLine(
keyValues["Code"][x]+
keyValues["Name"][x]+
keyValues["Age"][x]
);
LINQ's a hammer; not every problem is a nail.
ps if you have N keys, you can easily turn it into a
var keys = new[]{"Code","Name","Age","Foo","Bar"};
for(...)
foreach(var k in keys)
... //some concat here or use the values directly eg adding to your page
You could easily use Zip here. However, you could roll your own
public static IEnumerable<string> DoStuff<T, T2>(Dictionary<T, List<T2>> source)
{
var max = source.Values.Max(x => x?.Count ?? 0);
for (var i = 0; i < max; i++)
yield return string.Concat(source.Values.Select(x => x.ElementAtOrDefault(i)));
}
Usage
var results = DoStuff(keyvalues);
Console.WriteLine(string.Join(Environment.NewLine,results));
Output
1A20
2B30
3C40
4D50
or
public static IEnumerable<string> DoStuff<T>(List<T>[] source)
{
var max = source.Max(x => x?.Count ?? 0);
for (var i = 0; i < max; i++)
yield return string.Concat(source.Select(x => x.ElementAtOrDefault(i)));
}
...
var results = DoStuff(keyvalues.Values.ToArray());
Console.WriteLine(string.Join(Environment.NewLine,results));

Sequence of consecutive equal elements in C#

You are given an array with numbers. Find consecutive equal ones and print the maximal sequence of consecutive equal elements.How to found and print the maximal one.
int[] array = new int[] {1,1,2,3,555,4,34,4,4,4,6,6,888,8,8,8,8,7,7,77};
int start = 0;
....
for (int i = 0; i < array.Length; i++)
{
if (start == array[i])
{
Console.Write(start + " " + array[i] + " ");
}
start = array[i];
}
You could use this extension and a Lookup<TKey, TElement>:
public static IEnumerable<T> GetConsecutiveEqual<T>(this IEnumerable<T> seq)
{
if(!seq.Any())
yield break;
var comparer = EqualityComparer<T>.Default;
T last = seq.First();
using (IEnumerator<T> e = seq.GetEnumerator())
{
while (e.MoveNext())
{
if (comparer.Equals(last, e.Current))
{
yield return last;
yield return e.Current;
if(!e.MoveNext()) break;
}
last = e.Current;
}
}
}
which you can use in this way:
int[] array = new int[] { 1, 1, 2, 3, 555, 4, 34, 4, 4, 4, 6, 6, 888, 8, 8, 8, 8, 7, 7, 77,77 };
var conseqEquals = array.GetConsecutiveEqual(); ;
var lookup = conseqEquals.ToLookup(i => i);
var maxGroup = lookup.OrderByDescending(g => g.Count()).First();
string allNums = string.Join(",", maxGroup); // 8,8,8,8
If you want to see all with that max-count you can use this approach:
// note that the array now contains 4 consecutive 4, 4, 4, 4:
int[] array = new int[] { 1, 1, 2, 3, 555, 4, 34, 4, 4, 4, 4, 6, 6, 888, 8, 8, 8, 8, 7, 7, 77,77 };
var conseqEquals = array.GetConsecutiveEqual(); ;
int maxGroupCount = lookup.Max(g => g.Count());
var allWithMaxCount = lookup.Where(g => g.Count() == maxGroupCount)
.Select(mg => string.Join(",", mg));
string allNums = string.Join(" | ", allWithMaxCount); // 4,4,4,4 | 8,8,8,8
Update: you could also use this extension that is inspired by Enumerable.GroupBy and returns groups of adjacent/consecutive items:
public static IEnumerable<IGrouping<TKey, TSource>> GroupAdjacent<TSource, TKey>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector)
{
TKey last = default(TKey);
bool haveLast = false;
List<TSource> list = new List<TSource>();
foreach (TSource s in source)
{
TKey k = keySelector(s);
if (haveLast)
{
if (!k.Equals(last))
{
yield return new GroupOfAdjacent<TSource, TKey>(list, last);
list = new List<TSource>();
list.Add(s);
last = k;
}
else
{
list.Add(s);
last = k;
}
}
else
{
list.Add(s);
last = k;
haveLast = true;
}
}
if (haveLast)
yield return new GroupOfAdjacent<TSource, TKey>(list, last);
}
}
and the class used (put both in your extension library):
public class GroupOfAdjacent<TSource, TKey> : IEnumerable<TSource>, IGrouping<TKey, TSource>
{
public TKey Key { get; set; }
private List<TSource> GroupList { get; set; }
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return ((System.Collections.Generic.IEnumerable<TSource>)this).GetEnumerator();
}
System.Collections.Generic.IEnumerator<TSource> System.Collections.Generic.IEnumerable<TSource>.GetEnumerator()
{
foreach (var s in GroupList)
yield return s;
}
public GroupOfAdjacent(List<TSource> source, TKey key)
{
GroupList = source;
Key = key;
}
}
You use it similarly, but the extension-method can be handy in many situations:
var groupConsecutive = array.GroupAdjacent(i => i);
int maxGroupCount = groupConsecutive.Max(g => g.Count());
var allWithMaxCount = groupConsecutive.Where(g => g.Count() == maxGroupCount)
.Select(mg => string.Join(",", mg));
string allNums = string.Join(" | ", allWithMaxCount);

AllConsecutives on List<int>

I want to create an extension method like this
public static bool AllConsecutives(this IEnumerable<int> intValues )
This method should return true if all items in the list are consecutive (with no gaps)
some test cases
(new List<int>() {2, 3, 4, 5, 6}).AllConsecutives() == true
(new List<int>() {3, 7, 4, 5, 6}).AllConsecutives() == true //as it is not sensitive to list order
(new List<int>() {2, 3, 4, 7, 6}).AllConsecutives() == false //as the five is missing
int expected = intValues.Min();
foreach(int actual in intValues.OrderBy(x => x))
{
if (actual != expected++)
return false;
}
return true;
You can also verify, that collection has at least one item, before executing Min. Or you can sort items prior to taking min (in this case it will be first one, or will not be any, if collection is empty). Also in this case you will save one iteration for finding minimal value:
var sortedValues = intValues.OrderBy(x => x);
int expected = sortedValues.FirstOrDefault();
foreach (int actual in sortedValues)
{
if (actual != expected++)
return false;
}
return true;
Tried and seems to work with the given examples
public static bool AllConsecutives(this IEnumerable<int> intValues )
{
var ord = intValues.OrderBy(i => i);
int curV = ord.Min();
foreach(int x in ord)
{
if(x != curV)
return false;
curV++;
}
return true;
}
The list is consecutive if it does not contains duplicates and the difference between the max. and min. values is equal to the number of items in the list minus one, so:
public static bool AllConsecutives(this IEnumerable<int> intValues)
{
int minValue = Int32.MaxValue;
int maxValue = Int32.MinValue;
int count = 0;
HashSet<int> values = new HashSet<int>();
foreach (int intValue in intValues) {
if (values.Contains(intValue))
return false;
values.Add(intValue);
if (intValue > maxValue)
maxValue = intValue;
if (intValue < minValue)
minValue = intValue;
count++;
}
return (count == 0) || (maxValue-minValue+1 == count);
}
Error checking and using Linq:
public static class myExtension
{
public static bool AllConsecutives(this IEnumerable<int> targetList)
{
bool result = false;
if ((targetList != null) && (targetList.Any ()))
{
var ordered = targetList.OrderBy (l => l);
int first = ordered.First ();
result = ordered.All (item => item == first++);
}
return result;
}
}
// tested with
void Main()
{
Console.WriteLine ( (new List<int>() {2, 3, 4, 5, 6}).AllConsecutives() ); // true
Console.WriteLine ( (new List<int>() {3, 7, 4, 5, 6}).AllConsecutives() ); // true //as it is not sensitive to list order
Console.WriteLine ( (new List<int>() {2, 3, 4, 7, 6}).AllConsecutives() ); // false //as the five is missing
}
Something like this:
if (intValues.Count() <= 1)
return true;
var ordered = intValues.OrderBy(i => i).ToList();
return (ordered.First() + ordered.Count() - 1) == ordered.Last();
list.Sort();
return !list.Skip(1).Where((i, j) => (i != (list[j] + 1))).Any();
Another possible solution that doesn't requires sorting, is to use hashset, and while building the hashset you can hold the min value. With this the run time will be O(n).
This will not take care of duplicate values which you can just add a check while building the hashset and see if the hashset already contains the value.
HashSet<int> hash = new HashSet<int>();
int minValue = int.MaxValue;
foreach(int i in list)
{
if(minValue > i)
minValue = i;
hash.Add(i);
}
for(int count = 1; count < list.Count; ++count)
{
if(!hash.Contains(++minValue))
return false;
}
return true;

how to sort number to desired in c#?

for each element that their difference with former is less than three, order them in descending
numbers = {1,2,3,4,5,6,13,18,25,30,31,32,33}
desired = {6,5,4,3,2,1,13,18,25,33,32,31,30}
for example in numbers list ,Because difference between 6 and 5 is less than 3 sort them in descending
You can use LINQ:
var numbers = new int[] { 1, 2, 3, 4, 5, 6, 13, 18, 25, 30, 31, 32, 33 };
var result = numbers.GroupAdjacent((x, y) => y - x < 3)
.SelectMany(g => g.OrderByDescending(x => x))
.ToArray();
// result == { 6, 5, 4, 3, 2, 1, 13, 18, 25, 33, 32, 31, 30 }
with
static IEnumerable<IEnumerable<T>> GroupAdjacent<T>(
this IEnumerable<T> source, Func<T, T, bool> adjacent)
{
var g = new List<T>();
foreach (var x in source)
{
if (g.Count != 0 && !adjacent(g.Last(), x))
{
yield return g;
g = new List<T>();
}
g.Add(x);
}
yield return g;
}
hello that is too complex..
As i dont have the Visual studio so i wrote the code in javascript that will cover your requirement
<script>
var a = [1,2,3,4,5,6,13,18,25,30,31,32,33];
alert(custom_sort(a));
function custom_sort(a) {
var objArrayList = {};
var index = 0;
var where_i_am = 0;
objArrayList[index] = Array();
if(a[1]-a[0] < 3){
where_i_am = 1;
objArrayList[index].push(a[0]);
} else {
where_i_am = 2;
objArrayList[index].push(a[0]);
}
for(var i=1;i<a.length;i++) {
if(a[i]-a[i-1] < 3) {
if(where_i_am ==2) {
where_i_am = 1;
index++;
objArrayList[index] = Array();
}
if(where_i_am==1)
objArrayList[index].push(a[i]);
} else {
if(where_i_am==1) {
where_i_am =2;
index++;
objArrayList[index] = Array();
}
if(where_i_am==2) {
objArrayList[index].push(a[i]);
}
}
}
var new_array = new Array();
for(var obj in objArrayList) {
var array_val = objArrayList[obj];
if(array_val[1] - array_val[0] < 3) {
new_array = new_array.concat(sort_desc(array_val));
} else {
new_array = new_array.concat(sort_asc(array_val));
}
}
return new_array;
}
function sort_asc(array_val){
for(var i =0;i<array_val.length;i++) {
for(var j=0;j<array_val.length;j++) {
if(array_val[i] < array_val[j]) {
var temp = array_val[i];
array_val[i] = array_val[j];
array_val[j] = temp;
}
}
}
return array_val;
}
function sort_desc(array_val){
for(var i =0;i<array_val.length;i++) {
for(var j=0;j<array_val.length;j++) {
if(array_val[i] > array_val[j]) {
var temp = array_val[i];
array_val[i] = array_val[j];
array_val[j] = temp;
}
}
}
return array_val;
}
</script>
For me such algorithm looks very non standard so I feel there are few algorithms should be used to achieve good results in terms of complexity. There is one very important question - does an initial array already sorted?
Any way you can start by splitting array by sub arrays:
Split such array to multiple (with some indicator whether each one should be sorted
ASC or DESC)
Then if an initial array is not sorted:
Then sort all of sub arrays one by one using QuickSort, so you will get a set of sorted arrays
Then you can sort sub arrays using the first element of each so order of sub arrays should be saved (Hope I described it clear enough)
But if an initial array was sorted:
Merge sub arrays in initially preserved order

C# - How to get complement of two lists without identifiers

I have two lists of the same type. That type does not have an identifier or any other guaranteed way to programatically distinguish.
List A: {1, 2, 2, 3, 5, 8, 8, 8}
List B: {1, 3, 5, 8}
I want the items from A that are not in B.
Desired Result: {2, 2, 8, 8}
If the types had identifiers, I could use a statement like the following...
var result = listA
.Where(a => listB.Where(b => b.Id == a.Id).Count() == 0)
.ToList();
So far, the only way I can do this is with a loop where I add each item the number of times it doesn't appear in the original list.
foreach (var val in listB.Select(b => b.val).Distinct())
{
var countA = listA.Where(a => a.val == val).Count();
var countB = listB.Where(b => b.val == val).Count();
var item = listA.Where(a => a.val == val).FirstOrDefault();
for (int i=0; i<countA-countB; i++)
result.Add(item);
}
Is there a cleaner way to achieve this?
EDIT:
Here is a simplified version of the object in the lists. It's coming from a Web service that's hitting another system.
public class myObject
{
public DateTime SomeDate { get; set; }
public decimal SomeNumber; { get; set; }
public bool IsSomething { get; set; }
public string SomeString { get; set; }
}
The data I am receiving has the same values for SomeDate/SomeString and repeated values for SomeNumber and IsSomething. Two objects might have equal properties, but I need to treat them as distinct objects.
try this:
var listA = new List<Int32> {1, 2, 2, 3, 5, 8, 8, 8};
var listB = new List<Int32> {1, 3, 5, 8};
var listResult = new List<Int32>(listA);
foreach(var itemB in listB)
{
listResult.Remove(itemB);
}
What am I missing?
class Program
{
static void Main(string[] args)
{
List<int> a = new List<int>();
a.Add(1);
a.Add(2);
a.Add(2);
a.Add(3);
a.Add(5);
a.Add(8);
a.Add(8);
a.Add(8);
List<int> b = new List<int>();
b.Add(1);
b.Add(3);
b.Add(5);
b.Add(8);
foreach (int x in b)
a.Remove(x);
foreach (int x in a)
Console.WriteLine(x);
Console.ReadKey(false);
}
}
Are the objects same instances in both lists? If so you can use .Where(a => listB.Where(b => b == a).Count() == 0)
Or
.Where(a => !listB.Any(b => b == a))
You could sort both lists and then iterate through them both at the same time.
public IEnumerable<int> GetComplement(IEnumerable<int> a, IEnumerable<int> b)
{
var listA = a.ToList();
listA.Sort();
var listB = b.ToList();
listB.Sort();
int i=0,j=0;
while( i < listA.Count && j < listB.Count )
{
if(listA[i] > listB[j]) {yield return listB[j];j++;}
else if (listA[i] < listB[j]) {yield return listA[i]; i++; }
else {i++;j++;}
}
while(i < listA.Count)
{
yield return listA[i];
i++;
}
while(j < listB.Count)
{
yield return listB[j];
j++;
}
}
I don't know if this is "cleaner", but it should be more performant on large sets of data.
This is a bit nasty but it does what you want. Not sure about performance though.
var a = new List<int> { 1, 2, 2, 3, 5, 8, 8, 8 };
var b = new List<int> { 1, 3, 5, 8 };
var c = from x in a.Distinct()
let a_count = a.Count(el => el == x)
let b_count = b.Count(el => el == x)
from val in Enumerable.Repeat (x, a_count - b_count)
select val;
Why don't you implement your own equality comparer for your myObject:
public class YourTypeEqualityComparer : IEqualityComparer<myObject>
{
public bool Equals(myObject x, myObject y)
public int GetHashCode(myObject obj)
}
and then use it like this:
var list1 = new List<myObj>();
var list2 = new List<myObj>()
list1.RemoveAll(i =>
list2.Contains(list1),
new YourTypeEqualityComparer()
);
now list1 contains result.

Categories