/**
 * ```js
 * import from from '@adamburgess/linq'
 * const sequence = from(['an', 'iterable', 'here!']);
 * // now use all the methods on sequence!
 * ```
 *
 * See {@link AnySequence} for all the transformations you can do to a sequence.
 * @module
 */
/** A sequence of values.
 *
 * To create me, use from:
 * ```js
 * const sequence = from(['an', 'iterable', 'here!']);
 * // now use all the methods on sequence!
 * ```
*/
export interface AnySequence<T> extends Iterable<T> {
    /** Map each element to another
     *
     * ```js
     * from([2, 1, 5]).map(x => x + 1)
     * // => [3, 2, 6]
     * ```
     */
    map<TResult>(f: (arg: T, index: number) => TResult): Sequence<TResult>;
    /** Filters with a TS type assertion ('is'), narrowing to that type
     *
     * ```typescript
     * function isNumber(x: number | string): x is number {
     *     return typeof x === 'number';
     * }
     * from(['string', 12, 'str']).where(isNumber)
     * // => [12]
     * // sequence is now Sequence<number>
     * ```
     */
    where<TNarrowed extends T>(f: (arg: T | TNarrowed, index: number) => arg is TNarrowed): Sequence<TNarrowed>;
    /** Filters with and narrows to a type
     *
     * ```typescript
     * from(['string', 12, 'str']).where<number>(x => typeof x === 'number')
     * // => [12]
     * // sequence is now Sequence<number>
     * ```
     * note! must use generic type! otherwise this overload is not used, and the type is not narrowed.
     */
    where<TNarrowed extends T>(f: (arg: T | TNarrowed, index: number) => boolean): Sequence<TNarrowed>;
    /** Filters with a predicate that must return a truthy value
     *
     * ```js
     * from([5, 10, 20]).where(x => x >= 8)
     * // => [10, 20]
     * ```
     */
    where(f: (arg: T, index: number) => any): Sequence<T>;
    /** Reverses the sequence.
     *
     * ```js
     * from([2, 1, 5]).reverse()
     * // => [5, 1, 2]
     * ```
     */
    reverse(): Sequence<T>;
    /** Project each element to get a key, and group all items by that key.
     *
     * ```js
     * from([10, 15, 20]).groupBy(x => Math.trunc(x / 10))
     *                    .map(g => ({ key: g.key, values: Array.from(g) }))
     * // => [{ key: 1, values: [10, 15] },
     * //     { key: 2, values: [20] }]
     * ```
     */
    groupBy<TKey>(keySelector: (arg: T) => TKey): ArraySequence<KeySequence<TKey, T>>;
    /** Project each element to get a key, and group all items, each projected onto another type.
     *
     * ```js
     * from([10, 15, 20]).groupBy(x => Math.trunc(x / 10), x => x.toString())
     *                    .map(g => ({ key: g.key, values: Array.from(g) }))
     * // => [{ key: 1, values: ['10', '15'] },
     * //     { key: 2, values: ['20'] }]
     * ```
     */
    groupBy<TKey, TProject>(keySelector: (arg: T) => TKey, elementSelector: (arg: T) => TProject): ArraySequence<KeySequence<TKey, TProject>>;
    /** Sort the array in ascending order of the selector
     *
     * ```js
     * from([4, 1, 10]).orderBy(x => x)
     * // => [1, 4, 10]
     * ```
     */
    orderBy(keySelector: (arg: T) => string | number): OrderedSequence<T>;
    /** Sort the array in ascending order of the selector, with a custom comparer
     *
     * ```js
     * // Sort alphabetically, ignoring case.
     * from(['A xylophone', 'a frog', 'a zoo']).orderBy(x => x, new Intl.Collator('en', { sensitivity: 'base' }).compare)
     * // => ['a frog', 'A xylophone', 'a zoo']
     * ```
     */
    orderBy<TKey>(keySelector: (arg: T) => TKey, comparer: ICompare<TKey>): OrderedSequence<T>;
    /** Sort the array in descending order of the selector
     *
     * ```js
     * from([4, 1, 10]).orderByDescending(x => x)
     * // => [10, 4, 1]
     * ```
     */
    orderByDescending(keySelector: (arg: T) => string | number): OrderedSequence<T>;
    /** Sort the array in descending order of the selector, with a custom comparer
     *
     * ```js
     * // Sort reverse alphabetically, ignoring case.
     * from(['A xylophone', 'a frog', 'a zoo']).orderByDescending(x => x, new Intl.Collator('en', { sensitivity: 'base' }).compare)
     * // => ['a zoo', 'A xylophone', 'a frog']
     * ```
     */
    orderByDescending<TKey>(keySelector: (arg: T) => TKey, comparer: ICompare<TKey>): OrderedSequence<T>;
    /** Take a maximum amount of elements
     *
     * ```js
     * from([2, 1, 5]).take(2)
     * // => [2, 1]
     * ```
     */
    take(count: number): Sequence<T>;
    /** Take elements while the predicate is true, then skips the rest
     *
     * ```js
     * from([2, 3, 1, 5]).takeWhile(x => x >= 2)
     * // => [2, 3]
     * ```
     */
    takeWhile(predicate: (arg: T) => any): Sequence<T>;
    /** Skip a number of elements before letting the rest through
     *
     * ```js
     * from([2, 3, 1, 5]).skip(2)
     * // => [1, 5]
     * ```
     */
    skip(count: number): Sequence<T>;
    /** Skip elements while the predicate is true, then take the rest
     *
     * ```js
     * from([2, 3, 1, 5]).skipWhile(x => x >= 2)
     * // => [1, 5]
     * ```
     */
    skipWhile(predicate: (arg: T) => any): Sequence<T>;
    /** Append another iterable to the end
     *
     * ```js
     * from([1, 2]).append([3, 4])
     * // => [1, 2, 3, 4]
     * ```
     */
    append<TAppend>(iterable: Iterable<TAppend>): Sequence<T | TAppend>;
    /** Prepend another iterable to the start
     *
     * ```js
     * from([1, 2]).prepend([3, 4])
     * // => [3, 4, 1, 2]
     * ```
     */
    prepend<TPrepend>(iterable: Iterable<TPrepend>): Sequence<TPrepend | T>;
    /** Get all distinct elements of the sequence using strict equality. First value wins.
     *
     * ```js
     * from([1, 2, 2, 3]).distinct()
     * // => [1, 2, 3]
     * ```
     */
    distinct(): Sequence<T>;
    /** Get all distinct elements of the sequence, mapping each element to a key that will be used for strict equality. First value wins.
     *
     * ```js
     * from([10, 15, 20, 25, 30]).distinct(x => Math.trunc(x / 10))
     * // => [10, 20, 30]
     * ```
     */
    distinct(keySelector: (arg: T) => unknown): Sequence<T>;
    /** Project each element to an iterable/array, then flatten the result
     *
     * ```js
     * from(['1 2', '3 4']).flat(x => x.split(' '))
     * // => ['1', '2', '3', '4']
     * ```
     */
    flat<TProject>(projector: (input: T) => Iterable<TProject>): Sequence<TProject>;
    /** Correlates the elements of two sequences based on matching keys. An inner join.
     *
     * ```js
     * const appleTypes = [
     *     { name: 'green apple', id: 5 },
     *     { name: 'red apple', id: 2 },
     *     { name: 'yellow apple', id: 10 }
     * ];
     * const apples = [
     *     { name: 'golden delicious', type: 10 },
     *     { name: 'granny smith', type: 5 },
     *     { name: 'pink lady', type: 2 },
     *     { name: 'fuji', type: 2 },
     *     { name: 'unknown', type: 999 }
     * ];
     *
     * from(apples).join(
     *     appleTypes,
     *     apple => apple.type,
     *     type => type.id,
     *     (apple, type) => `${apple.name}: ${type.name}`
     * )
     * // => [ 'golden delicious: yellow apple',
     * //      'granny smith: green apple',
     * //      'pink lady: red apple',
     * //      'fuji: red apple' ]
     * ```
     */
    join<TInner, TKey, TResult>(innerSequence: Iterable<TInner>, outerKeySelector: (arg: T) => TKey, innerKeySelector: (arg: TInner) => TKey, resultSelector: (outer: T, inner: TInner) => TResult): Sequence<TResult>;
    /** Correlates the elements of two sequences based on matching keys, and groups everything in the other table.
     *
     * ```js
     * const appleTypes = [
     *     { name: 'green apple', id: 5 },
     *     { name: 'red apple', id: 2 },
     *     { name: 'yellow apple', id: 10 }
     * ];
     * const apples = [
     *     { name: 'golden delicious', type: 10 },
     *     { name: 'granny smith', type: 5 },
     *     { name: 'pink lady', type: 2 },
     *     { name: 'fuji', type: 2 },
     *     { name: 'unknown', type: 999 }
     * ];
     *
     * from(appleTypes).groupJoin(
     *     apples,
     *     type => type.id,
     *     apple => apple.type,
     *     (type, apples) => `${type.name}: ${apples.map(a => a.name).joinString(', ')}`
     * );
     * // => [ 'green apple: granny smith',
     * //      'red apple: pink lady, fuji',
     * //      'yellow apple: golden delicious' ]
     * ```
     */
    groupJoin<TInner, TKey, TResult>(innerSequence: Iterable<TInner>, outerKeySelector: (arg: T) => TKey, innerKeySelector: (arg: TInner) => TKey, resultSelector: (outer: T, inner: Sequence<TInner>) => TResult): Sequence<TResult>;
    /** Counts the number of elements in the sequence
     *
     * ```js
     * from([2, 1, 5]).count()
     * // => 3
     * from([]).count()
     * // => 0
     * ```
     */
    count(): number;
    /**
     * Converts this sequence to an array.
     * Note: If you are using this Sequence in a for..of loop, you do _not_ need this --
     *       you can use the sequence directly.
     *
     * ```js
     * from([2, 1, 5]).toArray()
     * // => [2, 1, 5]
     * ```
     */
    toArray(): T[];
    /** Converts this sequence to a Map. First key wins.
     *
     * ```js
     * from([{ k: 'a', v: 123 }, { k: 'b', v: 456 }]).toMap(x => x.k, x => x.v)
     * // => new Map([['a', 123], ['b', 456]])
     * ```
     */
    toMap<TKey, TElement>(keySelector: (arg: T) => TKey, elementSelector: (arg: T) => TElement): Map<TKey, TElement>;
    /** Converts this sequence into an object. First key wins.
     *
     * ```js
     * from([{ k: 'a', v: 123 }, { k: 'b', v: 456 }]).toObject(x => x.k, x => x.v)
     * // => { a: 123, b: 456 }
     * ```
     */
    toObject<TKey extends PropertyKey, TElement>(keySelector: (arg: T) => TKey, elementSelector: (arg: T) => TElement): Record<TKey, TElement>;
    /** Convert this sequence into a Set
     *
     * ```js
     * from([2, 1, 1, 5]).toSet()
     * // => new Set([2, 1, 5])
     * ```
     */
    toSet(): Set<T>;
    /** Map each element and convert the resulting sequence into a set
     *
     * ```js
     * from([2, 1, 1, 5]).toSet(x => x + 1)
     * // => new Set([3, 2, 6])
     * ```
     */
    toSet<TProject>(projector: (arg: T) => TProject): Set<TProject>;
    /** Get the first element in the sequence. Will throw if empty! Use firstOrDefault if no throw is wanted.
     *
     * ```js
     * from([2, 1, 5]).first()
     * // => 2
     * from([]).first()
     * // => throws
     * ```
     */
    first(): T;
    /** Get the first element in the sequence that matches a condition. Will throw if empty! Use firstOrDefault if no throw is wanted.
     *
     * ```js
     * from([2, 1, 5]).first(x => x >= 4)
     * // => 5
     * from([2, 1, 5]).first(x => x < 0)
     * // => throws
     * ```
     */
    first(predicate: (arg: T) => any): T;
    /** Get the first element in the sequence. If empty, returns undefined.
     *
     * ```js
     * from([2, 1, 5]).firstOrDefault()
     * // => 2
     * from([]).firstOrDefault()
     * // => undefined
     * ```
     */
    firstOrDefault(): T | undefined;
    /** Get the first element in the sequence that matches a condition. If empty or no matches, returns undefined.
     *
     * ```js
     * from([2, 1, 5]).firstOrDefault(x => x >= 4)
     * // => 5
     * from([2, 1, 5]).firstOrDefault(x => x < 0)
     * // => undefined
     * ```
     */
    firstOrDefault(predicate: (arg: T) => any): T | undefined;
    /** Get the _only_ element in the sequence. Will throw if empty or more than one element! Use singleOrDefault if no throw is wanted.
     *
     * ```js
     * from([2]).single()
     * // => 2
     * from([2, 1, 5]).single()
     * // => throws
     * from([]).single()
     * // => throws
     * ```
     */
    single(): T;
    /** Get the _only_ element in the sequence that matches a condition. Will throw if empty or more than one element! Use singleOrDefault if no throw is wanted.
     *
     * ```js
     * from([2, 1, 5]).single(x => x >= 4)
     * // => 5
     * from([2, 1, 5]).single(x => x >= 1)
     * // => throws
     * ```
     */
    single(predicate: (arg: T) => any): T;
    /** Get the _only_ element in the sequence. Returns undefined if empty or more than one element.
     *
     * ```js
     * from([2]).singleOrDefault()
     * // => 2
     * from([2, 1, 5]).singleOrDefault()
     * // => undefined
     * ```
     */
    singleOrDefault(): T | undefined;
    /** Get the _only_ element in the sequence that matches a condition. Returns undefined if empty or more than one element.
     *
     * ```js
     * from([2, 1, 5]).singleOrDefault(x => x >= 4)
     * // => 5
     * from([2, 1, 5]).singleOrDefault(x => x >= 1)
     * // => undefined
     * ```
     */
    singleOrDefault(predicate: (arg: T) => any): T | undefined;
    /** Get the last element in the sequence. Will throw if empty! Use lastOrDefault if no throw is wanted.
     *
     * ```js
     * from([2, 1, 5]).last()
     * // => 5
     * from([]).last()
     * // => throws
     * ```
     */
    last(): T;
    /** Get the last element in the sequence that matches a condition. Will throw if empty! Use lastOrDefault if no throw is wanted.
     *
     * ```js
     * from([2, 1, 5]).last(x => x < 4)
     * // => 1
     * from([]).last(x => x < 0)
     * // => throws
     * ```
     */
    last(predicate: (arg: T) => any): T;
    /** Get the last element in the sequence. If empty, returns undefined.
     *
     * ```js
     * from([2, 1, 5]).lastOrDefault()
     * // => 5
     * from([]).lastOrDefault()
     * // => undefined
     * ```
     */
    lastOrDefault(): T | undefined;
    /** Get the last element in the sequence that matches a condition. If empty or no matches, returns undefined.
     *
     * ```js
     * from([2, 1, 5]).lastOrDefault(x => x < 4)
     * // => 1
     * from([2, 1, 5]).lastOrDefault(x => x < 0)
     * // => undefined
     * ```
     */
    lastOrDefault(predicate: (arg: T) => any): T | undefined;
    /** True if all elements pass the predicate
     *
     * ```js
     * from([2, 1, 5]).all(x => x >= 1)
     * // => true
     * from([2, 1, 5]).all(x => x >= 2)
     * // => false
     * ```
     */
    all(predicate: (arg: T) => any): boolean;
    /** True if any elements pass the predicate
     *
     * ```js
     * from([2, 1, 5]).any(x => x >= 4)
     * // => true
     * from([2, 1, 5]).any(x => x <= 0)
     * // => false
     * ```
     */
    any(predicate: (arg: T) => any): boolean;
    /** True if the sequence is not empty
     *
     * ```js
     * from([2, 1, 5]).any()
     * // => true
     * from([]).any()
     * // => false
     * ```
     */
    any(): boolean;
    /** True if no elements pass the predicate
     *
     * ```js
     * from([2, 1, 5]).none(x => x === 0)
     * // => true
     * from([2, 1, 5]).none(x => x === 1)
     * // => false
     * ```
     */
    none(predicate: (arg: T) => any): boolean;
    /** True if the element is in the sequence. Checked with ===.
     *
     * ```js
     * from([2, 1, 5]).contains(1)
     * // => true
     * from([{ a: '1' }]).contains({ a: '1' })
     * // => false (strict equality. use any with a custom search instead.)
     * ```
     */
    contains(value: T): boolean;
    /** Projects each element to a number and sums the sequence. If empty, returns 0.
     *
     * ```js
     * from(['2', '1', '5']).sum(x => parseFloat(x))
     * // => 8
     * ```
     */
    sum(f: (arg: T) => number): number;
    /** Projects each element to a number and averages the sequence. If empty, throws.
     *
     * ```js
     * from(['2', '1', '6']).average(x => parseFloat(x))
     * // => 3
     * ```
     */
    average(f: (arg: T) => number): number;
    /** Projects each element to a number and finds the max of the sequence. If empty, throws.
     *
     * ```js
     * from(['2', '1', '5']).max(x => parseFloat(x))
     * // => 5
     * ```
     */
    max(f: (arg: T) => number): number;
    /** Projects each element to a number and finds the min of the sequence. If empty, throws.
     *
     * ```js
     * from(['2', '1', '5']).min(x => parseFloat(x))
     * // => 1
     * ```
     */
    min(f: (arg: T) => number): number;
    /** Finds the minimum element in the sequence according to a selector. Equivalent (but faster) to orderBy(f).first().
     *
     * ```js
     * from([{ key: 'A', value: 2 }, { key: 'B', value: 1 }, { key: 'C', value: 10 }]).minBy(x => x.value)
     * // => { key: 'B', value: 1 }
     * ```
     */
    minBy(f: (arg: T) => number): T;
    /** Finds the maximum element in the sequence according to a selector. Equivalent (but faster) to orderByDescending(f).first().
     *
     * ```js
     * from([{ key: 'A', value: 2 }, { key: 'B', value: 1 }, { key: 'C', value: 10 }]).maxBy(x => x.value)
     * // => { key: 'C', value: 10 }
     * ```
     */
    maxBy(f: (arg: T) => number): T;
    /** Iterates through this sequence. */
    [Symbol.iterator](): Iterator<T>;
}
/** A sequence of numbers */
export interface NumberSequence<T> extends AnySequence<T> {
    /** Sums every number in the sequence. If empty, returns 0.
     *
     * ```js
     * from([2, 1, 5]).sum()
     * // => 8
     * ```
     */
    sum(): number;
    /** Averages the sequence. If empty, throws.
     *
     * ```js
     * from([2, 1, 6]).average()
     * // => 3
     * ```
     */
    average(): number;
    /** Finds the maximum in the sequence. If empty, throws.
     *
     * ```js
     * from([2, 1, 5]).max()
     * // => 5
     * ```
     */
    max(): number;
    /** Finds the minimum in the sequence. If empty, throws.
     *
     * ```js
     * from([2, 1, 5]).min()
     * // => 1
     * ```
     */
    min(): number;
}
declare type UnwrapIterable<T> = [T] extends [Iterable<infer U>] ? U : never;
/** A sequence of iterable elements */
export interface ArraySequence<T> extends AnySequence<T> {
    /** Project each element to an iterable/array, then flatten the result
     *
     * ```js
     * from([[1, 2], [3, 4]]).flat(x => [...x, 0])
     * // => [1, 2, 0, 3, 4, 0]
     * ```
     */
    flat<TProject>(projector: (input: T) => Iterable<TProject>): Sequence<TProject>;
    /** Flatten the sequence
     *
     * ```js
     * from([[1, 2], [3, 4]]).flat()
     * // => [1, 2, 3, 4]
     * ```
     */
    flat(): Sequence<UnwrapIterable<T>>;
}
/** A sequence of strings */
export interface StringSequence<T> extends AnySequence<T> {
    /** Joins the elements with an optional separator
     *
     * ```js
     * from(['a', 'b', 'c']).joinString(', ')
     * // => 'a, b, c'
     * ```
     */
    joinString(separator?: string): string;
}
export interface WithKey<TKey> {
    /** The key that this set was grouped by */
    readonly key: TKey;
}
export interface WithOrderedMethods<T> {
    /** Order the sequence by another key, ascending.
     *
     * ```js
     * // Sort by length of the string, then alphabetical order
     * from(['two', 'one', 'thirteen', 'five']).orderBy(x => x.length).thenBy(x => x)
     * // => ['one', 'two', 'five', 'thirteen']
     * ```
     */
    thenBy(keySelector: (arg: T) => string | number): OrderedSequence<T>;
    /** Order the sequence by another key, ascending, with a custom comparer.
     *
     * See orderBy for an example.
     */
    thenBy<TKey>(keySelector: (arg: T) => TKey, comparer: ICompare<TKey>): OrderedSequence<T>;
    /** Order the sequence by another key, descending.
     *
     * ```js
     * // Sort by length of the string, then reverse alphabetical order
     * from(['one', 'two', 'thirteen', 'five']).orderBy(x => x.length).thenByDescending(x => x)
     * // => ['two', 'one', 'five', 'thirteen']
     * ```
     */
    thenByDescending(keySelector: (arg: T) => string | number): OrderedSequence<T>;
    /** Order the sequence by another key, descending, with a custom comparer.
     *
     *
     * See orderBy for an example.
     */
    thenByDescending<TKey>(keySelector: (arg: T) => TKey, comparer: ICompare<TKey>): OrderedSequence<T>;
}
declare type DoesExtend<T, TWant> = [T] extends [TWant] ? T : never;
declare type DoesExtendAny<T> = any extends T ? never : T;
declare type ExtendsCarefully<T, TWant> = DoesExtendAny<DoesExtend<T, TWant>>;
/** A sequence of values. */
export declare type Sequence<T> = ExtendsCarefully<T, number> extends never ? (ExtendsCarefully<T, string> extends never ? (ExtendsCarefully<T, Iterable<unknown>> extends never ? AnySequence<T> : ArraySequence<T>) : StringSequence<T>) : NumberSequence<T>;
/** A sequence with a key. Obtained from groupBy. */
export declare type KeySequence<TKey, TElement> = WithKey<TKey> & Sequence<TElement>;
/** An ordered sequence. Can use thenBy to continue ordering. */
export declare type OrderedSequence<T> = WithOrderedMethods<T> & Sequence<T>;
/** Gets the sequence type.
 * ```typescript
 * type MySequence = Sequence<string>
 * type MySequenceType = SequenceType<MySequence>
 * // MySequenceType === string
 * ```
 */
export declare type SequenceType<T> = T extends Sequence<infer Y> ? Y : never;
/** Compares two values.
 *  If a comes before b, the result should be negative.
 *  If both values are equal, the result should be zero.
 *  If a comes after b, the result should be positive.
 */
export declare type ICompare<T> = (a: T, b: T) => number;
/** Start the sequence. Can be used with an array or any iterable.
 *
 * ```js
 * // Import me:
 * import from from '@adamburgess/linq'
 * // Arrays:
 * from([1, 2, 3])
 * // Objects that support the Iterable protocol:
 * from(new Map())
 * // Generators:
 * from(function*() { yield 'generated values' })
 * ```
*/
export declare function from<T>(it: Iterable<T>): Sequence<T>;
export default from;
