programing

LINQ를 사용하여 인덱스를 가져오는 방법은 무엇입니까?

topblog 2023. 5. 23. 21:17
반응형

LINQ를 사용하여 인덱스를 가져오는 방법은 무엇입니까?

다음과 같은 데이터 소스를 고려할 때:

var c = new Car[]
{
  new Car{ Color="Blue", Price=28000},
  new Car{ Color="Red", Price=54000},
  new Car{ Color="Pink", Price=9999},
  // ..
};

LINQ로 특정 조건을 충족하는 첫 번째 차량의 지수를 어떻게 찾을 수 있습니까?

편집:

나는 이런 것을 생각할 수 있었지만 끔찍해 보입니다:

int firstItem = someItems.Select((item, index) => new    
{    
    ItemName = item.Color,    
    Position = index    
}).Where(i => i.ItemName == "purple")    
  .First()    
  .Position;

이것을 단순하고 오래된 루프로 해결하는 것이 최선일까요?

myCars.Select((v, i) => new {car = v, index = i}).First(myCondition).index;

또는 약간 짧은 것.

myCars.Select((car, index) => new {car, index}).First(myCondition).index;

또는 조금 더 짧은 것.

myCars.Select((car, index) => (car, index)).First(myCondition).index;

간단히 수행:

int index = List.FindIndex(your condition);

예.

int index = cars.FindIndex(c => c.ID == 150);

IEnumerable순서 집합이 아닙니다.
대부분의 IE 숫자는 순서대로 나열되지만 일부는 다음과 같습니다(예:Dictionary또는HashSet) 그렇지 않습니다.

따라서 LINQ에는 다음이 없습니다.IndexOf방법.

그러나 직접 작성할 수 있습니다.

///<summary>Finds the index of the first item matching an expression in an enumerable.</summary>
///<param name="items">The enumerable to search.</param>
///<param name="predicate">The expression to test the items against.</param>
///<returns>The index of the first matching item, or -1 if no items match.</returns>
public static int FindIndex<T>(this IEnumerable<T> items, Func<T, bool> predicate) {
    if (items == null) throw new ArgumentNullException("items");
    if (predicate == null) throw new ArgumentNullException("predicate");

    int retVal = 0;
    foreach (var item in items) {
        if (predicate(item)) return retVal;
        retVal++;
    }
    return -1;
}
///<summary>Finds the index of the first occurrence of an item in an enumerable.</summary>
///<param name="items">The enumerable to search.</param>
///<param name="item">The item to find.</param>
///<returns>The index of the first matching item, or -1 if the item was not found.</returns>
public static int IndexOf<T>(this IEnumerable<T> items, T item) { return items.FindIndex(i => EqualityComparer<T>.Default.Equals(item, i)); }
myCars.TakeWhile(car => !myCondition(car)).Count();

효과가 있습니다!생각해 보세요.첫 번째 일치 항목의 인덱스는 일치하지 않는 항목의 개수와 같습니다.

이야기 시간

저도 당신의 질문에서 이미 제안한 끔찍한 표준 해결책이 싫어요.받아들여진 대답처럼 나는 약간의 수정을 가했지만 평범하고 오래된 루프를 선택했습니다.

public static int FindIndex<T>(this IEnumerable<T> items, Predicate<T> predicate) {
    int index = 0;
    foreach (var item in items) {
        if (predicate(item)) break;
        index++;
    }
    return index;
}

대신 항목 수를 반환합니다.-1적수가 없을 때하지만 지금은 이 사소한 짜증은 무시해요.사실 이 경우에는 끔찍한 표준 솔루션이 충돌하기 때문에 경계를 벗어난 우수한 인덱스를 반환하는 을 고려하고 있습니다.

ReSharper는 루프가 LINQ 표현으로 변환될 수 있다고 말합니다.대부분의 경우 기능이 가독성을 악화시키지만, 이번에는 놀라운 결과를 얻었습니다.제트 브레인즈에 대한 칭찬입니다.

분석.

장점

  • 간결하다.
  • 다른 LINQ와 결합 가능
  • 피함new익명 개체 입력
  • 술어가 처음으로 일치할 때까지만 열거형을 평가합니다.

그래서 저는 읽을 수 있는 상태로 시간과 공간에서 최적이라고 생각합니다.

단점

  • 처음에는 명확하지 않습니다.
  • 반환 안 함-1적수가 없을 때.

물론 항상 확장 방법 뒤에 숨길 수 있습니다.그리고 일치하는 항목이 없을 때 가장 잘 할 수 있는 것은 상황에 따라 크게 달라집니다.

제가 여기에 기여할 것입니다... 왜죠? 단지 :p Any LINQ 확장에 기반한 다른 구현이며 대리자이기 때문입니다.여기 있습니다.

public static class Extensions
{
    public static int IndexOf<T>(
            this IEnumerable<T> list, 
            Predicate<T> condition) {               
        int i = -1;
        return list.Any(x => { i++; return condition(x); }) ? i : -1;
    }
}

void Main()
{
    TestGetsFirstItem();
    TestGetsLastItem();
    TestGetsMinusOneOnNotFound();
    TestGetsMiddleItem();   
    TestGetsMinusOneOnEmptyList();
}

void TestGetsFirstItem()
{
    // Arrange
    var list = new string[] { "a", "b", "c", "d" };

    // Act
    int index = list.IndexOf(item => item.Equals("a"));

    // Assert
    if(index != 0)
    {
        throw new Exception("Index should be 0 but is: " + index);
    }

    "Test Successful".Dump();
}

void TestGetsLastItem()
{
    // Arrange
    var list = new string[] { "a", "b", "c", "d" };

    // Act
    int index = list.IndexOf(item => item.Equals("d"));

    // Assert
    if(index != 3)
    {
        throw new Exception("Index should be 3 but is: " + index);
    }

    "Test Successful".Dump();
}

void TestGetsMinusOneOnNotFound()
{
    // Arrange
    var list = new string[] { "a", "b", "c", "d" };

    // Act
    int index = list.IndexOf(item => item.Equals("e"));

    // Assert
    if(index != -1)
    {
        throw new Exception("Index should be -1 but is: " + index);
    }

    "Test Successful".Dump();
}

void TestGetsMinusOneOnEmptyList()
{
    // Arrange
    var list = new string[] {  };

    // Act
    int index = list.IndexOf(item => item.Equals("e"));

    // Assert
    if(index != -1)
    {
        throw new Exception("Index should be -1 but is: " + index);
    }

    "Test Successful".Dump();
}

void TestGetsMiddleItem()
{
    // Arrange
    var list = new string[] { "a", "b", "c", "d", "e" };

    // Act
    int index = list.IndexOf(item => item.Equals("c"));

    // Assert
    if(index != 2)
    {
        throw new Exception("Index should be 2 but is: " + index);
    }

    "Test Successful".Dump();
}        

여기 제가 방금 작성한 작은 확장자가 있습니다.

public static class PositionsExtension
{
    public static Int32 Position<TSource>(this IEnumerable<TSource> source,
                                          Func<TSource, bool> predicate)
    {
        return Positions<TSource>(source, predicate).FirstOrDefault();
    }
    public static IEnumerable<Int32> Positions<TSource>(this IEnumerable<TSource> source, 
                                                        Func<TSource, bool> predicate)
    {
        if (typeof(TSource) is IDictionary)
        {
            throw new Exception("Dictionaries aren't supported");
        }

        if (source == null)
        {
            throw new ArgumentOutOfRangeException("source is null");
        }
        if (predicate == null)
        {
            throw new ArgumentOutOfRangeException("predicate is null");
        }
        var found = source.Where(predicate).First();
        var query = source.Select((item, index) => new
            {
                Found = ReferenceEquals(item, found),
                Index = index

            }).Where( it => it.Found).Select( it => it.Index);
        return query;
    }
}

그러면 이렇게 부르면 됩니다.

IEnumerable<Int32> indicesWhereConditionIsMet = 
      ListItems.Positions(item => item == this);

Int32 firstWelcomeMessage ListItems.Position(msg =>               
      msg.WelcomeMessage.Contains("Hello"));

다음은 항목을 찾을 수 없을 때 -1을 반환하는 가장 높은 투표를 받은 답변의 구현입니다.

public static int FindIndex<T>(this IEnumerable<T> items, Func<T, bool> predicate)
{
    var itemsWithIndices = items.Select((item, index) => new { Item = item, Index = index });
    var matchingIndices =
        from itemWithIndex in itemsWithIndices
        where predicate(itemWithIndex.Item)
        select (int?)itemWithIndex.Index;

    return matchingIndices.FirstOrDefault() ?? -1;
}

언급URL : https://stackoverflow.com/questions/2471588/how-to-get-index-using-linq

반응형