Reputation: 863
I have a list that looks like this :
public static void main {
int itemToBeSearched = 4;
List<Item> list = new List<Item>();
list.Add(new Item { Id = 1, Name = "name1" });
list.Add(new Item { Id = 2, Name = "name2" });
list.Add(new Item { Id = 3, Name = "name1" });
list.Add(new Item { Id = 4, Name = "name1" });
list.Add(new Item { Id = 5, Name = "name1" });
list.Add(new Item { Id = 6, Name = "name3" });
list.Add(new Item { Id = 7, Name = "name1" });
//I need help in getting a return here
var subList = GetAdjacentList(list, itemToBeSearched);
}
public List<Item> GetAdjacentList(List list, int itemToBeSearched)
{
//Need help with the logic or linq here that would give a return such as
return List<Item> list = new List<Item>();
list.Add(new Item { Id = 3, Name = "name1" });
list.Add(new Item { Id = 4, Name = "name1" });
list.Add(new Item { Id = 5, Name = "name1" });
}
And the desired output should be: a sublist out of this that only contains Item2, Item3
and Item4
.
I only wanted a return of a sublist that only contains Item3, Item4
and Item5
because they are adjacent to each other with the same name which is name1
.
Item1
and Item7
are not included even though they have the name as name1
because it isn't close to the adjacent group.
Can someone help me solve this using Linq or otherwise?
Upvotes: 1
Views: 134
Reputation:
You can use simple backward and forward loops to find the adjacent items of a specific name within a given range and return a List<Item>
with the same order of the items:
public List<Item> GetAdjacentList(List<Item> list, int itemToBeSearched, int range)
{
var result = new List<Item>();
var tar = list.FirstOrDefault(x => x.Id == itemToBeSearched);
if (tar == null) return result;
var indx = list.IndexOf(tar);
for (var i = indx - 1; i >= (indx - range); i--)
{
if (i >= 0 && list[i].Name.Equals(tar.Name,
StringComparison.CurrentCultureIgnoreCase))
result.Insert(0, list[i]);
else
break;
}
result.Add(list[indx]);
for (var i = indx + 1; i <= (indx + range) ; i++)
{
if (i < list.Count() && list[i].Name.Equals(tar.Name,
StringComparison.CurrentCultureIgnoreCase))
result.Add(list[i]);
else
break;
}
return result;
}
The outputs of the given List<Item>
example (rang = 1
):
The adjacent items of ID 1 => 1 (name1)
The adjacent items of ID 2 => 2 (name2)
The adjacent items of ID 3 => 3, 4 (name1)
The adjacent items of ID 4 => 3, 4, 5 (name1)
The adjacent items of ID 5 => 4, 5 (name1)
The adjacent items of ID 6 => 6 (name3)
The adjacent items of ID 7 => 7 (name1)
Upvotes: 0
Reputation: 18155
You haven't mentioned what happens when there are two groups of consecutive Ids. For example,
Case 1 : Retrieve as two separate groups
Assuming you could like to retrieve both islands of groups as separate collections, you could modify the method as following.
public IEnumerable<IEnumerable<Item>> GetAdjacentList(List<Item> list, int itemToBeSearched)
{
var nameToBeSearched = list.First(x=>x.Id==itemToBeSearched).Name;
return list.Where(x=>x.Name.Equals(nameToBeSearched))
.GroupWhile((x, y) => y.Id - x.Id == 1)
.Where(x=>x.Count()>1);
}
Where GroupWhile
is defined as
public static class Extensions
{
public static IEnumerable<IEnumerable<T>> GroupWhile<T>(this IEnumerable<T> seq, Func<T,T,bool> condition)
{
T prev = seq.First();
List<T> list = new List<T>() { prev };
foreach(T item in seq.Skip(1))
{
if(condition(prev,item)==false)
{
yield return list;
list = new List<T>();
}
list.Add(item);
prev = item;
}
yield return list;
}
}
Output
Case 2 : Retrieve as a single merged group
In case you want to the two island groups to be merged as one, then you could modify the above method as
public IEnumerable<Item> GetAdjacentList(List<Item> list, int itemToBeSearched)
{
var nameToBeSearched = list.First(x=>x.Id==itemToBeSearched).Name;
return list.Where(x=>x.Name.Equals(nameToBeSearched))
.GroupWhile((x, y) => y.Id - x.Id == 1)
.Where(x=>x.Count()>1).SelectMany(x=>x);
}
Output
Upvotes: 1
Reputation: 38767
There's probably a simpler way of doing it, but I came up with this. It doesn't use any LINQ, but it does maintain the order of the list items (assuming that's important).
public static List<Item> GetAdjacentList(List<Item> list, int itemToBeSearched)
{
List<Item> result = new List<Item>();
// find the index of the item we're searching for
int itemIdx = list.FindIndex(i => i.Id == itemToBeSearched);
if (itemIdx == -1)
{
return result; // not found, return empty list
}
// store the item for comparisons
Item item = list[itemIdx];
// loop backwards starting from the current item ann going to the 0th element
for (int i = itemIdx; i >= 0; --i)
{
// if this item is the search item, or the name matches, add it to the list
if (i == itemIdx || list[i].Name == item.Name)
{
result.Add(list[i]);
}
else
{
// exit the loop as we've run out of items with the same name
break;
}
}
if (result.Count > 0)
{
result.Reverse(); // we've added items in reverse order, so reverse the list
}
// loop through all the items from the next item to the end of the list
for (int i = itemIdx + 1; i < list.Count; ++i)
{
// if the name matches add it to the result list
if (list[i].Name == item.Name)
{
result.Add(list[i]);
}
else
{
// we've reached the end of matching names so break
break;
}
}
// return the result
return result;
}
Upvotes: 2