Let's say I have these two arrays:
var array1 = new[] {"A", "B", "C"};
var array2 = new[] {"A", "C", "D"};
I would like to get the differences between the two. I know I could write this in just a few lines of code, but I want to make sure I'm not missing a built in language feature or a LINQ extension method.
Ideally, I would end up with the following three results:
Items not in array1, but are in array2 ("D")
Items not in array2, but are in array1 ("B")
Items that are in both
If you've got LINQ available to you, you can use Except and Distinct. The sets you asked for in the question are respectively:
- array2.Except(array1)
- array1.Except(array2)
- array1.Intersect(array2)
from the MSDN 101 LINQ samples....
public void Linq52() {
int[] numbersA = { 0, 2, 4, 5, 6, 8, 9 };
int[] numbersB = { 1, 3, 5, 7, 8 };
IEnumerable<int> aOnlyNumbers = numbersA.Except(numbersB);
Console.WriteLine("Numbers in first array but not second array:");
foreach (var n in aOnlyNumbers) {
Console.WriteLine(n);
}
}
Here are the benchmarks of LINQ extension methods. The results were obtained during the development of a real program.
The tests:
2 lists (lst1 and lst2) each approximately 250000 objects. Each object (class Key) contains a string and an integer. The second list mostly contains the same entries as the first one, but some new entries are added and some are removed.
I tested the Except extension method.
var except = lst2.Except(lst1);
List lst = except.ToList();
These 2 lines produced 600 items list of “new additions”. I timed it using the StopWatch object. The speed is astonishing:220 ms. The computer I used is by no means a “speedy Gonzales”. Core 2 Duo T7700 – 2.4GHz.
Note:
Here is the class Key, which implements IEquatable i-face.
public class Key : IEquatable<Key>
{
public int Index { get; private set; }
public string Name { get; private set; }
public Key(string keyName, int sdIndex)
{
this.Name = keyName;
this.Index = sdIndex;
}
// IEquatable implementation
public bool Equals(Key other)
{
//Check whether the compared object is null.
if (Object.ReferenceEquals(other, null)) return false;
//Check whether the compared object references the same data.
if (Object.ReferenceEquals(this, other)) return true;
//Check whether the products' properties are equal.
return Index.Equals(other.Index) && Name.Equals(other.Name);
}
// If Equals() returns true for a pair of objects
// then GetHashCode() must return the same value for these objects.
public override int GetHashCode()
{
//Get hash code for the name field if it is not null.
int hashKeyName = Name == null ? 0 : Name.GetHashCode();
//Get hash code for the index field.
int hashKeyIndex = Index.GetHashCode();
//Calculate the hash code for the Key.
return hashKeyName ^ hashKeyIndex;
}
}
I've had to do things similar to this with very large sets of data. If you're dealing with a few thousand or so, use the Linq stuff since it's much clearer. But if you know that your arrays are pre-sorted, running a merge like this can do it significantly faster, since it only makes one pass through the data and doesn't need to allocate as much memory as the Linq version.
int iA = 0;
int iB = 0;
List<int> inA = new List<int>();
List<int> inB = new List<int>();
List<int> inBoth = new List<int>();
while (iA < numbersA.Length && iB < numbersB.Length)
{
if (numbersA[iA] < numbersB[iB])
{
inA.Add(numbersA[iA++]);
}
else if (numbersA[iA] == numbersB[iB])
{
inBoth.Add(numbersA[iA++]);
++iB;
}
else
{
inB.Add(numbersB[iB++]);
}
}
while (iA < numbersA.Length)
{
inA.Add(numbersA[iA++]);
}
while (iB < numbersB.Length)
{
inB.Add(numbersB[iB++]);
}
Again, this is really only needed if you are dealing with hundreds of thousands of values.
Another solution would be like below as well
int[] arr1 = new int[] { 45, 26, 99, 55, 36 };
int[] arr2 = new int[] { 45, 26, 99, 20, 36 };
var res = arr1.Union(arr2).Except(arr1.Intersect(arr2));
Related
If I have 2 int arrays a and b and they contain data like this..
a[0] = 1
a[1] = 3
a[2] = 7
b[0] = 6
b[1] = 3
b[2] = 5
How can I check if all the pairs of numbers are unique e.g. that each combination of a[i] and b[i] at the same index is not repeated in the rest of the array... So the above data would pass but if I introduced this below it would fail..
a[24] = 7
b[24] = 5
Because this combination already exists in the array at index 2. Can I do this in LINQ?
If the order of the values in the pairs over the two arrays is significant (i.e. a[0] == 1, b[0] == 2 is considered different from a[0] == 2, b[0] == 1) then one way to check for uniqueness using Linq is as follows:
bool unique = a.Zip(b).Distinct().Count() == a.Length;
If the order of the values in the pairs is NOT significant, it's slightly more fiddly:
bool unique = a.Zip(b).DistinctBy(
x => (Math.Min(x.First, x.Second), Math.Max(x.First,x.Second)))
.Count() == a.Length;
These solutions assume that missing values in one of the arrays will be ignored.
(Note: DistinctBy() is only available in .Net 6.0 or later, or via a NuGet package.)
I've managed to work this out and solve this with :
a.Zip(b, (aPos, bPos) => new { aPosition = aPos, bPosition = bPos }).Distinct().Count()
This will tell me how many distinct sets of both values at the same index and so I can work the rest out from here.
Apologies if my question wasn't clear.
try this sample :
int [] aa = a.Distinct().ToArray();
or :
public static bool HasDuplicates<T>(IList<T> items)
{
Dictionary<T, bool> map = new Dictionary<T, bool>();
for (int i = 0; i < items.Count; i++)
{
if (map.ContainsKey(items[i]))
{
return true; // has duplicates
}
map.Add(items[i], true);
}
return false; // no duplicates
}
and call :
string[] strings = new[] { "1", "2", "3" };
Utility.HasDuplicates(strings)// this will return false
int[] items=new []{1,2,3,1};
Utility.HasDuplicates(items)// this will return true
I have a class that has a bunch of different variables and a couple lists 1 in particular holds ints(positionInts)
I also have a list(teamsList) for holding objects I have created from that class
now I would like to sort the team's list by positions values
Hopefully, I'm not being too vague as the project I'm working on is full of not well-written code so it can be hard to explain.
This function orders the list according to your precondition.
private List<String> OrderList(List<String> teams, int[] positions)
{
List<String> orderedTeams;
Dictionary<int, string> teamsToOrder = new Dictionary<int, string>();
int position = 0;
foreach (string team in teams)
{
teamsToOrder.Add(positions[position], teams[position]);
position = position + 1;
}
orderedTeams = teamsToOrder.OrderByDescending(team => team.Key).Select(team => team.Value).ToList();
return orderedTeams;
}
If I understand your question correctly, then you have list of arbitrary type, for example list of strings:
var teamsList = new List<String> { "team1", "team2", "team3", "team4" };
Next up, you have enumeration of integers:
var positionInts = new[] { 2, 3, 1, 0 };
And your goal is to order teamsList based on sequence numbers of the positionInts. In that case you can use following method:
static IEnumerable<T> OrderBySequence<T>(IEnumerable<T> source, IEnumerable<Int32> sequence)
{
for (var i = 0; i < Math.Min(source.Count(), sequence.Count()); i++)
{
var s = sequence.ElementAt(i);
if (s > -1 && s < source.Count())
{
yield return source.ElementAt(s);
}
}
}
Which will produce:
team3
team4
team2
team1
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 a function that is simply meant to print out a dictionary of frequent item sets in an easy-to-understand fashion. The goal is to order first by the size of the dictionary key and then by the lexicographical order of a list of numbers. The issue arises in the ThenBy statement as the commented out "hello" will get printed indefinitely. If I change the ThenBy to not use the comparer and simply use another int or string value, it works fine, so I'm clearly doing something wrong.
public static void printItemSets(Dictionary<List<int>, int> freqItemSet)
{
List<KeyValuePair<List<int>, int>> printList = freqItemSet.ToList();
printList = printList.OrderBy(x => x.Key.Count)
.ThenBy(x => x.Key, new ListComparer())
.ToList();
}
The code for the ListComparer is as follows:
public class ListComparer: IEqualityComparer<List<int>>, IComparer<List<int>>
{
public int Compare(List<int> a, List<int> b)
{
int larger = a.Count > b.Count ? 1: -1;
for (int i = 0; i < a.Count && i < b.Count; i++)
{
if (a[i] < b[i])
{
return -1;
}
else if (a[i] > b[i])
{
return 1;
}
else { }
}
return larger;
}
}
VERY simple test case:
int[] a = {1, 3, 5};
int[] b = { 2, 3, 5 };
int[] c = { 1, 2, 3, 5 };
int[] d = { 2, 5 };
int[] e = { 1, 3, 4 };
List<int> aL = a.ToList<int>();
List<int> bL = b.ToList<int>();
List<int> cL = c.ToList<int>();
List<int> dL = d.ToList<int>();
List<int> eL = e.ToList<int>();
Dictionary<List<int>, int> test = new Dictionary<List<int>, int>(new ListComparer());
test.Add(aL, 1);
test.Add(bL, 1);
test.Add(cL, 1);
test.Add(dL, 1);
test.Add(eL, 1);
The issue is that ListComparer is not checking if the arrays are the same. The same array is being passed in twice for both x and y. Checking if x and y are equal will resolve your issue.
Your comparer doesn't handle equal items. If the items are equal the order of the two items is what determines which is considered "larger". The comparer is thus not "reflexive". Being reflexive is a property sorting algorithms rely on.
The first line should be var larger = a.Count.CompareTo(b.Count); instead, so that truly equal lists will return 0 rather than either -1 or 1.
i want to add new row to table but by passing both normal variables and array variable like the example below
int value1=1;
int value2=2;
int[] numbers = new int[] {3, 4, 5};
DataTable1.Rows.Add(value1,value2,numbers) // numbers as single items so the row will contain 5 values (1,2,3,4,5)
so should i build a new array and pass it ? or there a code spell to do that ?
thanks
This helper method will create a list from 1 to 5:
public IEnumerable<T> GetItemsAndCollectionsAsItems<T>(params object[] itemsAndCollections)
{
var result = new List<T>();
foreach (var itemOrCollection in itemsAndCollections)
{
var collection = itemOrCollection as IEnumerable<T>;
if (collection == null)
{
result.Add((T)itemOrCollection);
}
else
{
result.AddRange(collection);
}
}
return result;
}
And you call it this way:
int value1 = 1;
int value2 = 2;
int[] numbers = new int[] { 3, 4, 5 };
// Returns 1,2,3,4,5
IEnumerable<int> values = GetItemsAndCollectionsAsItems<int>(value1, value2, numbers);
Not sure to be happen with this Int Array but yah, have a look on this link, which stores data same as you want. Simply some tricky things have to do.