I have an array structured like this:
{"nick", "sender", "message"}
arranged into a List<string[]>.
What I want to do, is to search the list using the 0 index value of the array (ie nick).
I tried using:
list.Find(i => i[0].Equals(nick))
but this doesn't seem to do anything.
How would I do this?
I think this what you want
list.Where(i => i[0] == "nick")
It will return a IEnumerable<string[]> where nick if the first element in each string[]
list.Where(x => x[0].Equals(nick));
I guess you're after:-
list.Find(i => i.Equals("nick"))
I'm also guessing that this isn't what you mean....
Perhaps you have something more like this:-
static void Main(string[] args)
{
var test = new List<string[]>() {
new String[3] { "a", "b", "b" },
new String[3] { "a", "c", "c" },
new String[3] { "b", "b", "c" },
new String[3] { "a", "d", "d" },
new String[3] { "x", "y", "z" }
};
var foundFirst = test.Find(i => i[0].Equals("a"));
var foundAll = test.Where(i => i[0].Equals("a"));
}
Related
I have a sequence of elements
string[]:
new string[] { "A", "B", "C", "D" }
And there is also an object consisting of such several sequences
object:
List<Test> obj = new List<Test>()
{
new Test() {Lists = new List<string>() { "A", "B" } },
new Test() {Lists = new List<string>() { "A", "C" } },
new Test() {Lists = new List<string>() { "C" } }
};
I want to find the missing element ("D") in all object collections.
That's what I got:
private static List<string> FindeMissingElements()
{
string nonExistentElement = null;
List<string> nonExistentElements = new List<string>();
foreach (var elemArr in arr)
{
foreach (var elemObj in obj)
{
if (elemObj.Lists.Any(a => a.Contains(elemArr)))
{
nonExistentElement = null;
break;
}
nonExistentElement = elemArr;
}
if (nonExistentElement != null)
nonExistentElements.Add(nonExistentElement);
}
return nonExistentElements;
}
I would like to simplify the code and use LINQ if possible...
First, I would flatten the sources into a list so I have a collection of actual values. To do this, it's best to use SelectMany (tests is the original list, and domain is the array of possible elements)
var sourceElements = tests.SelectMany(test => test.Lists);
This will get the Lists properties for each test, and join all the results together, so in your example, you'll have a result of
["A", "B", "A", "C", "C"]
You can use Distinct to only get unique items, so the code is
var sourceElements = tests.SelectMany(test => test.Lists).Distinct();
Now, the only thing left to do is to find the items that are in the domain but not in the sourceElements, i.e. their set difference. That can easily be done with the Except method.
var missing = domain.Except(sourceElements);
So, to put everything together, your method should be:
private static IEnumerable<string> FindMissingElements(
IEnumerable<Test> tests, IEnumerable<string> domain)
{
var sourceElements = tests.SelectMany(test => test.Lists).Distinct();
var missing = domain.Except(sourceElements)
return missing;
}
Here is a working example.
//Flatten the complex object - Get all the elements from the complex object into an IEnumerable collection
//Flatten the list --
var listB = (from lm in obj select lm.Lists).SelectMany(it => it.ToList());
//Below list A is input elements
//use IEnumerable except extension as below - missingElements IEnumerable will have your elements from complex object --
string[] listA = new string[] { "A", "B", "C", "D", "E" };
var missinElements = listA.Except(listB);
I think this can help you:
var list1 = new string[] { "A", "B", "C", "D" }
var nonExistentElements = new List<string>(list1);
obj.ForEach(o => nonExistentElements = nonExistentElements.Except(o.Lists).ToList());
I Try and test your question like this:
private static List<string> FindeMissingElements()
{
var objectArray = new[] {"A", "B", "C", "D"};
var obj = new List<Test>()
{
new Test() {Lists = new List<string>() { "A", "B" } },
new Test() {Lists = new List<string>() { "A", "C" } },
new Test() {Lists = new List<string>() { "C" } }
};
var missingValues = objectArray.Where(x => !obj.Any(c => c.Lists.Any(v => v == x))).ToList();
return missingValues.any() ? missingValue:missingValue = new List<string>();
}
With 1 row you can find missing value.
GoodLuck.
Say I have a list of strings
1,2,3,a,b,a,b,c,1,2
I have a second list of strings
a,b,c
I want to remove the second list from the first resulting in
1,2,3,a,b,1,2
What's the best way to do this with with two List<string>?
Most methods/questions/answers I see involve lists revolve around individual lines of second list being removed from the first (all a's... all b's... all c's...).
I don't want that... I only want to remove those where the a followed by b followed by c.
Edit:
Couple caveats: The Second List is generally two or three strings and CAN appear multiple times (Say, instead, the second list is 1,2. It's contained in the first list twice).
var list = new List<string>(new[] { "1", "2", "3", "a", "b", "a", "b", "c", "1", "2" });
var sublist = new List<string>(new[] { "a", "b", "c" });
var start = -1;
var index = 0;
while (index < list.Count - sublist.Count)
{
for (int i = 0; i < sublist.Count; i++)
{
if (list[i + index] == sublist[i] && i == 0)
{
start = i + index;
}
else if (list[i + index] != sublist[i])
{
start = -1;
index++;
break;
}
}
if (start != -1)
{
list.RemoveRange(start, sublist.Count);
index -= sublist.Count;
}
}
foreach (var item in list)
{
Console.Write(item + ",");
}
With hack:
var list = new List<string>(new[] { "1", "2", "3", "a", "b", "a", "b", "c", "1", "2" });
var sublist = new List<string>(new[] { "a", "b", "c" });
var a = string.Join("#", list);
var b = string.Join("#", sublist);
var result =
new List<string>(a.Replace(b, string.Empty).Split(new[] { '#' }, StringSplitOptions.RemoveEmptyEntries));
foreach (var item in result)
{
Console.Write(item + ",");
}
This solution has very bad perfomance, but it can work for small lists.
With this few lines you could achieve the same. First convert it to String and then replace with 2nd string and convert it back to char array.
List<string> listA = new List<string>() { "1", "2", "3", "a", "b", "a", "b", "c", "1", "2" };
List<string> listB = new List<string>() { "a", "b", "c" };
string strA = string.Join("", listA);
string strB = string.Join("", listB);
strA = strA.Replace(strB, string.Empty);
List<string> resultList = strA.ToCharArray().Select(c => c.ToString()).ToList();
Below code if you need to support full fledged strings
List<string> listA = new List<string>() { "abc1", "2abc2", "3", "a", "b", "a", "b", "c", "1", "2" };
List<string> listB = new List<string>() { "a", "b", "c" };
string strA = string.Join(",", listA);
string strB = string.Join(",", listB) ;
strA = strA.Replace(strB, string.Empty).Replace(",,", ",");
List<string> resultList = strA.Split(',').ToList();
Removes multiple matches if this is what you expect. I'm not thrilled with the implementation, but it appears to work. I used a Stack (last in first out) because I'm lazy.
List<string> target = new List<string> { "1", "2", "3", "a", "b", "a", "b", "c", "1", "2", "a", "b", "c", "1" };
List<string> match = new List<string> { "a", "b", "c" };
Stack<int> matchIndexes = new Stack<int>();
for (int x = 0; x < target.Count - match.Count; x++)
{
int matches = 0;
for (int y = 0; y < match.Count; y++)
{
if (target[x + y] != match[y])
{
break;
}
else
{
matches++;
}
}
if (matches == match.Count)
{
matchIndexes.Push(x);
}
}
while(matchIndexes.Count > 0)
{
int index = matchIndexes.Pop();
target.RemoveRange(index, match.Count);
}
Just iterate through each item in list 2, and remove it from list 1.
foreach(string listItem in list2)
{
list1.Remove(listItem);
}
I have a list of strings List{"X", "W", "C", "A", "D", "B" } and I have another list of strings List{"A", "B", "C", "D"} that tells how the first list must be ordered. But the second list has only four items in it. I would like my first list to be ordered like this:
A, B, C, D, X, W. Actually the last two letters X and W doesn't matter how they are ordered, but they should be at the end of the list.
I tried this:
var newList = list1.OrderBy(i=>list2.IndexOf(i));
but this gives me only four items.
Your current code will give you 6 items. However, it will put X and W in the beginning since they have an index of -1 in list 2.
Here is how to fix that:
var list1 = new List<string> {"X", "W", "C", "A", "D", "B"};
var list2 = new List<string> {"A", "B", "C", "D"};
var newList = list1.OrderBy(x =>
{
var index = list2.IndexOf(x);
if (index == -1)
index = Int32.MaxValue;
return index;
})
.ToList();
One more way along with others.
List<string> list1 = new List<string>() {"X", "W", "C", "A", "D", "B" } ;
List<string> list2 = new List<string>() { "A", "B", "C", "D" } ;
var newList = list2.Intersect(list1)
.Union(list1.Except(list2));
Check Demo
This should work:
var newList = list1.OrderBy(i => {
var x = list2.IndexOf(i);
if(x == -1)
return int.MaxValue;
return x; });
Result (from LinqPad):
Hi I have allLists that contains lists of string I want to find common items among these string lists
i have tried
var intersection = allLists
.Skip(1)
.Aggregate(
new HashSet<string>(allLists.First()),
(h, e) => { h.IntersectWith(e); return h);`
and also intersection ( hard code lists by index) all of them did not work when I tried
var inter = allLists[0].Intersect(allLists[1]).Intersect(allLists[2])
.Intersect(allLists[3]).ToList();
foreach ( string s in inter) Debug.WriteLine(s+"\n ");
So how am I going to do this dynamically and get common string items in the lists;
is there a way to avoid Linq?
Isn't this the easiest way?
var stringLists = new List<string>[]
{
new List<string>(){ "a", "b", "c" },
new List<string>(){ "d", "b", "c" },
new List<string>(){ "a", "e", "c" }
};
var commonElements =
stringLists
.Aggregate((xs, ys) => xs.Intersect(ys).ToList());
I get a list with just "c" in it.
This also handles the case if elements within each list can be repeated.
I'd do it like this:
class Program
{
static void Main(string[] args)
{
List<string>[] stringLists = new List<string>[]
{
new List<string>(){ "a", "b", "c" },
new List<string>(){ "d", "b", "c" },
new List<string>(){ "a", "e", "c" }
};
// Will contian only 'c' because it's the only common item in all three groups.
var commonItems =
stringLists
.SelectMany(list => list)
.GroupBy(item => item)
.Select(group => new { Count = group.Count(), Item = group.Key })
.Where(item => item.Count == stringLists.Length);
foreach (var item in commonItems)
{
Console.WriteLine(String.Format("Item: {0}, Count: {1}", item.Item, item.Count));
}
Console.ReadKey();
}
}
An item is a common item if it occurs in all groups hence the condition that its count must be equal to the number of groups:
.Where(item => item.Count == stringLists.Length)
EDIT:
I should have used the HashSet like in the question. For lists you can replace the SelectMany line with this one:
.SelectMany(list => list.Distinct())
I came up with this, using LINQ:
records.RemoveAll(x =>
!(x.MyList.Contains(values[0]) || x.MyList.Contains(values[1]) || x.MyList.Contains(values[2]) || ... )
);
records is a List<CustomObject>, that has a List<string> MyList
values is an OrderedDictionary, though it could easily be a Dictionary<string,string> instead, if there is a solution that doesn't require accessing it by index.
My problem with this code is that I don't know the size of values ahead of time, and can't hardcode it like this. Is there a way to use a for or foreach loop here? Or, how else can I achieve this line of code?
I think it will be something like
records.RemoveAll(x =>
!(values.Any(v=>x.MyList.Contains(v)))
);
Try this:
var values = new OrderedDictionary()
{
{ "A", null },
{ "B", null },
{ "C", null },
{ "D", null },
{ "E", null },
};
var records = new List<CustomObject>
{
new CustomObject{ Id = 1, MyList = new List<string>(){ "A", "B" }},
new CustomObject{ Id = 2, MyList = new List<string>(){ "C", "F" }},
new CustomObject{ Id = 3, MyList = new List<string>(){ "G", "H" }}
};
records.RemoveAll(record => !record.MyList.Any(item => values.Contains(item)));
foreach (var record in records)
{
Console.WriteLine("Id={0}, MyList={1}",
record.Id, String.Join(", ", record.MyList.ToArray()));
}
This works as well...
records.Except(values.Select(v => v.Value)).ToList().ForEach(a => records.Remove(a.ToString())) ;