Split the data into batches and process each batch in its own thread.

    Args:
        input_list: An input object that has a list-like interface (indexing and slicing).
        item_mapper: (optional) A function to apply to each item in the batch.
        batch_mapper: (optional) A function to apply to each batch. Either item_mapper or batch_mapper must be set.
        flatten: Whether to unwrap individual batch results or keep them grouped by batch.
        n_jobs: The number of parallel processing jobs. -1 will use the number of CPUs on the system.
        batch_size: The maximum number of input items in each batch. -1 will store all data as a single batch.
        **kwargs: Additional keyword arguments to joblib.Parallel.

    Returns:
        A list representing the combined output from the mapper function called on all input items of each batch.

def map_batch_parallel(input_list, batch_size, item_mapper=None, batch_mapper=None, flatten=True, n_jobs=-1, **kwargs):
    """
    Split the data into batches and process each batch in its own thread.

    Args:
        input_list: An input object that has a list-like interface (indexing and slicing).
        item_mapper: (optional) A function to apply to each item in the batch.
        batch_mapper: (optional) A function to apply to each batch. Either item_mapper or batch_mapper must be set.
        flatten: Whether to unwrap individual batch results or keep them grouped by batch.
        n_jobs: The number of parallel processing jobs. -1 will use the number of CPUs on the system.
        batch_size: The maximum number of input items in each batch. -1 will store all data as a single batch.
        **kwargs: Additional keyword arguments to joblib.Parallel.

    Returns:
        A list representing the combined output from the mapper function called on all input items of each batch.
    """

    # We must specify either how to process each batch or how to process each item.
    if item_mapper is None and batch_mapper is None:
        raise ValueError('You should specify either batch_mapper or item_mapper.')

    if batch_mapper is None:
        batch_mapper = _default_batch_mapper

    batches = split_into_batches(input_list, batch_size, batch_storage_dir='')
    all_batch_results = Parallel(n_jobs=n_jobs, **kwargs)(
        delayed(batch_mapper)(batch['data'], item_mapper)
        for batch in progressbar(
            batches,
            desc='Batches',
            total=len(batches),
            file=sys.stdout,
        )
    )

    # Unwrap the individual batch results if necessary.
    if flatten:
        final_result = []
        for batch_result in all_batch_results:
            final_result.extend(batch_result)
    else:
        final_result = all_batch_results

    return final_result