Search code examples
c#linqjoindelegatesfunc

Delegate func in LINQ


I have a problem with optimizing my code). Is it possible to create a method that returns necessary outerKeySelector (i => i.ItemId1, i => i.ItemId2 etc.) as func for Join that depends from ItemType ?

Thanks!

    public class ItemModel
    {
        public long ItemId1 { get; set; }
        public long ItemId2 { get; set; }
        public long ItemId3 { get; set; }
        public long ItemId4 { get; set; }
    }

    public class IdModel
    {
        public long Id { get; set; }
        public decimal Quantity { get; set; }
    }

    public enum ItemType
    {
        Item1,
        Item2,
        Item3,
        Item4
    }
 

I believe it could be much shorter)

private static IEnumerable<IdModel> GetResult(IEnumerable<ItemModel> itemList, IEnumerable<IdModel> idList, ItemType itemType)
        {
            return itemType switch
            {
                ItemType.Item1 => itemList.Join(idList,
                    i => i.ItemId1,
                    j => j.Id,
                    (i, j) => new IdModel()
                    {
                        Id = j.Id,
                        Quantity = j.Quantity
                    }),
                ItemType.Item2 => itemList.Join(idList,
                    i => i.ItemId2,
                    j => j.Id,
                    (i, j) => new IdModel()
                    {
                        Id = j.Id,
                        Quantity = j.Quantity
                    })
            };
        }

Solution

  • I wouldn't suggest shorter is necessarily more optimal, but I sometimes prefer shorter to more verbose anyway.

    With a little preparation work creating a Dictionary<> to map ItemTypes to accessor delegates, you can remove your switch expression.

    private static Dictionary<ItemType, Func<ItemModel,long>> ItemIdAccessMap = new() {
        { ItemType.Item1, (ItemModel item) => item.ItemId1 },
        { ItemType.Item2, (ItemModel item) => item.ItemId2 },
        { ItemType.Item3, (ItemModel item) => item.ItemId3 },
        { ItemType.Item4, (ItemModel item) => item.ItemId4 },
    };
    

    I prefer reversing the order of the Join since you are throwing away the itemList members that match anyway.

    private static IEnumerable<IdModel> GetResult(IEnumerable<ItemModel> itemList, IEnumerable<IdModel> idList, ItemType itemType)
        => idList.Join(
                    itemList,
                    id => id.Id,
                    ItemIdAccessMap[itemType],
                    (id, item) => id
           );
    

    If you are concerned with having all ItemType members being handled, you could use Reflection to create the delegates dynamically, at the expense of some startup runtime.