Related
For example I have this as List<List<int>>:
[2,4,4,2,5]
[1,3,6,3,8]
[0,3,9,0,0]
Should return the sum but only taking cells assuming that the cell count is always the same:
[3, 10, 19, 5, 13]
I am trying to find an easy way to solve this using Linq if it is possible because I am doing this with a lot of for loops and if conditions and I am complicating myself.
Is there a possible way to achieve this using Linq?
Linq approach
List<List<int>> items = new List<List<int>>() {
new List<int> { 2, 4, 4, 2, 5 },
new List<int> { 1, 3, 6, 3, 8 },
new List<int> { 0, 3, 9, 0, 0 } };
List<int> result = Enumerable.Range(0, items.Min(x => x.Count)).Select(x => items.Sum(y => y[x])).ToList();
var xx = new List<List<int>>() {
new List<int>() { 2, 4, 4, 2, 5 },
new List<int>() { 1, 3, 6, 3, 8 },
new List<int>() { 0, 3, 9, 0, 0 },
};
var y = xx.Aggregate((r, x) => r.Zip(x).Select(p => p.First + p.Second).ToList());
I am doing this with a lot of for loops and if conditions and I am complicating myself.
You can accomplish it by using a single for loop.
Two possible approaches to achieve that are:
Approach 1
Creating an array with a capacity equal to the size of either of the lists in the original list collection
Filling the array with 0s
Looping through all lists in the original list collection, aggregating the sum for each index
Approach 2
Creating a list based on the first list in the original list collection
Looping through all subsequent lists in the original list collection, aggregating the sum for each index
Both approaches benefit from the assumption given in the question post:
[...] assuming that the cell count is always the same
If your original list collection is defined as a List<List<int>>:
List<List<int>> valuesCollection = new()
{
new() { 2, 4, 4, 2, 5 },
new() { 1, 3, 6, 3, 8 },
new() { 0, 3, 9, 0, 0 },
};
, the two approaches may be implemented as follows:
Approach 1
var indexCount = valuesCollection[0].Count;
var sums = new int[indexCount];
Array.Fill(sums, 0);
foreach (var values in valuesCollection)
{
for (var i = 0; i < sums.Length; i++)
{
sums[i] += values[i];
}
}
Approach 2
Note: Uses namespace System.Linq
var sums = valuesCollection[0].ToList();
foreach (var values in valuesCollection.Skip(1))
{
for (var i = 0; i < sums.Count; i++)
{
sums[i] += values[i];
}
}
Using either approach, sums's resulting content will be { 3, 10, 19, 5, 13 }.
Example fiddle here.
This question already has answers here:
Getting the "diff" between two arrays in C#?
(5 answers)
Closed 2 years ago.
Let say, I'm having 2 different arrays. The first is
x[] = {0, 1, 2, 3, 4} and the second one is y[] = {1, 3, 4}. The output that I expected is z[] = {0, 2}. How can i do this in C#?
Note: I'm also expecting the z[] elements are ordered ascending.
You can do it using Linq,
int[] x = new int[]{0, 1, 2, 3, 4};
int[] y = new int[]{1, 3, 4};
var result = x.Where(i => !y.Contains(i)).ToArray();
Console.WriteLine(string.Join(", ", result));
Using Set operation Except()
var result = x.Except(y);
If you are using Except(), then read below note from MSDN
This method returns those elements in first that don't appear in
second. It doesn't return those elements in second that don't appear
in first. Only unique elements are returned.
.Net Fiddle
The below code snippet gives, sorted unmatched elements in two arrays as result;
int[] x = new int[] { 1, 2, 2, 3, 4, 0 };
int[] y = new int[] { 1, 3, 3, 4, 5 };
int[] result = x.Except(y).Union(y.Except(x)).ToArray();
Array.Sort(result);
Console.WriteLine(string.Join(' ', result)); // Output: 0 2 5
I have two lists:
var list1 = new List<int> { 0, 1, 2 };
var list2 = new List<int> { 1, 2, 3 };
I want to be able to check if the ending chunk of list1 is present at the start of list2. After that I want to delete one of the chunks from any of the lists, merging both into a third list (sequentially, list1 + list2).
var list3 = list1.Something(list2);
//Returns 0,1,2,3 instead of 0,1,2,1,2,3
There's another problem, one list can be smaller than the other, such as:
0,1,2,3 <-- 2,3,4 = 0,1,2,3,4
5,6 <-- 6,7,8 = 5,6,7,8
And of course, both lists can be different:
0,1,2 <-- 5,6,7 = 0,1,2,5,6,7
[empty] <-- 1,2 = 1,2
Is there any method provided by .Net Framework that allows me to do that?
If not, could you help me create one?
The end and start can only "kill" each other if they are sequentially equal.
Example, if list1 ends in 1,2 and list2 starts with 2,1 they are not equal.
So, Distinct() is not helpful.
My use case:
private List<int> Cut(this List<int> first, List<int> second)
{
//Code
return new List<int>();
}
internal List<int> MergeKeyList()
{
var keyList = new List<int>() {0, 1, 2};
var newList = new List<int>() {1, 2, 3};
return keyList.InsertRange(keyList.Count, keyList.Cut(newList));
}
Would be much more efficient with for loops .. but whatever:
keyList.TakeWhile((_, i) => !keyList.Skip(i).SequenceEqual(newList.Take(keyList.Count - i)))
.Concat(newList)
Try this:
void Main()
{
var keyList = new List<int>() {0, 1, 2};
var newList = new List<int>() {1, 2, 3};
var result = keyList.Cut(newList);
}
public static class Ex
{
public static List<int> Cut(this List<int> first, List<int> second)
{
var skip =
second
.Select((x, n) => new { x, n })
.Where(xn => xn.x == first.Last())
.Where(xn =>
first
.Skip(first.Count - xn.n - 1)
.SequenceEqual(second.Take(xn.n + 1)))
.Reverse()
.Select(xn => xn.n + 1)
.FirstOrDefault();
return first.Concat(second.Skip(skip)).ToList();
}
}
result becomes:
Also:
{ 0, 1, 2 } & { 1, 2, 1, 2, 3 } => { 0, 1, 2, 1, 2, 3 }
{ 0, 1, 2, 1 } & { 1, 2, 1, 2, 3 } => { 0, 1, 2, 1, 2, 3 }
I need to find a way to return the longest match found in number of sets/lists (values returns only once) when the order of items is important.
the list is not cyclic.
A match is a sequence of values that exists in all the lists and maintains the same order of elements in all the lists.
e.g. 1:
List<int> list1 = new List<int> { 1, 2, 3, 4, 7, 9 };
List<int> list2 = new List<int> { 1, 2, 5, 6, 3, 4, 7, 9 };
List<int> list3 = new List<int> { 1, 2, 3, 6, 8, 9 };
List<int> list4 = new List<int> { 1, 2, 5, 6, 8, 9 };
result { 1, 2 }
e.g. 2:
List<int> list1 = new List<int> { 2, 3, 6, 8, 1, 18 };
List<int> list2 = new List<int> { 2, 3, 4, 6, 8, 1, 18, 19, 17, 14 };
List<int> list3 = new List<int> { 2, 5, 6, 8, 1, 18, 16, 13, 14 };
List<int> list4 = new List<int> { 2, 6, 8, 1, 18, 19, 17, 14 };
result { 6, 8, 1, 18 }
The match doesn't have to be found at the beginning or at the end and can be on any part of any list.
I hope that I explained my problem good enough :)
Thanks!
You can build a map from pairs of ints to a count of how many of the lists they appear adjacent in.
Pseudo-code:
For each list L {
For each adjacent pair (x, y) in L {
Counts[x, y] += 1
}
}
Now you can iterate through the first list (or the shortest list), and find the longest run such that each adjacent pair (x, y) in the run with Counts[x, y] showing that the pair appears in every list.
Pseudo-code:
run = []
best_run = []
For x in L[0] {
if len(run) is zero or Counts[run[len(run)-1], x] == number of lists {
run = run + x
} else {
run = [x]
}
if run is longer than best_run {
best_run = run
}
}
This works given the assumption in the question that no integer appears twice in the same list.
This algorithm runs in O(N) time, where N is the sum of the lengths of all the lists.
Here's my approach.
First I need a way to compare lists:
public class ListCompare<T> : IEqualityComparer<List<T>>
{
public bool Equals(List<T> left, List<T> right)
{
return left.SequenceEqual(right);
}
public int GetHashCode(List<T> list)
{
return list.Aggregate(0, (a, t) => a ^ t.GetHashCode());
}
}
Next a method to produce all subsequences of a source list:
Func<List<int>, IEnumerable<List<int>>> subsequences = xs =>
from s in Enumerable.Range(0, xs.Count)
from t in Enumerable.Range(1, xs.Count - s)
select xs.Skip(s).Take(t).ToList();
Now I can create a list of lists:
var lists = new [] { list1, list2, list3, list4, };
Finally a query that pulls it all together:
var answer =
lists
.Skip(1)
.Aggregate(
subsequences(lists.First()),
(a, l) => a.Intersect(subsequences(l), new ListCompare<int>()))
.OrderByDescending(x => x.Count)
.FirstOrDefault();
Given the sample data provided in the question this produces the expected results.
First generate an ordered combination of int from the shortest list
Compare the lists other than shortest list with the combination. For easy comparison of lists I just convert to string and use string.Contains()
Return immediately if find the match as the items left are next order or the shorter one.
public static List<int> GetLongestMatch(params List<int>[] all)
{
var shortest = all.Where(i => i.Count == all.Select(j => j.Count).Min()).First();
var permutations = (from length in Enumerable.Range(1, shortest.Count)
orderby length descending
from count in Enumerable.Range(1, shortest.Count - length + 1)
select shortest.Skip(count - 1).Take(length).ToList())
.ToList();
Func<List<int>, string> stringfy = (list) => { return string.Join(",", list.Select(i => i.ToString()).ToArray()); };
foreach (var item in permutations)
{
Debug.WriteLine(string.Join(", ", item.Select(i => i.ToString()).ToArray()));
if (all.All(list => stringfy(list).Contains(stringfy(item))))
{
Debug.WriteLine("Matched, skip process and return");
return item;
}
}
return new List<int>();
}
Usage
var result = GetLongestMatch(list1, list2, list3, list4);
Result
2, 3, 6, 8, 1, 18
2, 3, 6, 8, 1
3, 6, 8, 1, 18
2, 3, 6, 8
3, 6, 8, 1
6, 8, 1, 18
Matched, skip process and return
List<int> one //1, 3, 4, 6, 7
List<int> second //1, 2, 4, 5
How to get all elements from one list that are present also in second list?
In this case should be: 1, 4
I talk of course about method without foreach. Rather linq query
You can use the Intersect method.
var result = one.Intersect(second);
Example:
void Main()
{
List<int> one = new List<int>() {1, 3, 4, 6, 7};
List<int> second = new List<int>() {1, 2, 4, 5};
foreach(int r in one.Intersect(second))
Console.WriteLine(r);
}
Output:
1
4
static void Main(string[] args)
{
List<int> one = new List<int>() { 1, 3, 4, 6, 7 };
List<int> second = new List<int>() { 1, 2, 4, 5 };
var result = one.Intersect(second);
if (result.Count() > 0)
result.ToList().ForEach(t => Console.WriteLine(t));
else
Console.WriteLine("No elements is common!");
Console.ReadLine();
}