Paula Kristin
Paula Kristin

Reputation: 863

How to get the list of adjacent items with the same name only

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

Answers (3)

user10216583
user10216583

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

Anu Viswan
Anu Viswan

Reputation: 18155

You haven't mentioned what happens when there are two groups of consecutive Ids. For example,

enter image description here

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

enter image description here

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

enter image description here

Upvotes: 1

ProgrammingLlama
ProgrammingLlama

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

Related Questions