class_docstr: Step that reduces a dimension instead of manually looping on it.
  
  Using this step is equivalent to doing `sum(dact_data, [])` for each dact_data
  that might be a IDT, DIT, or EOT of a DACT (DAtaConTainer) to flatten the data,
  then the data is by default unflattened at the end of the loop in _did_process.
class_docstr_tok: ["Step","that","reduces","a","dimension","instead","of","manually","looping","on","it",".","Using","this","step","is","equivalent","to","doing","`","sum","(","dact_data",",","[","]",")","`","for","each","dact_data","that","might","be","a","IDT",",","DIT",",","or","EOT","of","a","DACT","(","DAtaConTainer",")","to","flatten","the","data",",","then","the","data","is","by","default","unflattened","at","the","end","of","the","loop","in","_did_process","."]
docstr: Flatten data container before any processing is done on the wrapped step, using lists.
code: def _will_process(
      self,
      data_container: DACT[
          Optional[Union[IDT, List[IDT]]],
          Optional[List[DIT]],
          Optional[List[EOT]],
      ],
      context: CX,
  ) -> Tuple["BaseTransformer", DACT]:
      """
      Flatten data container before any processing is done on the wrapped step, using lists.
      """
      data_container, context = super()._will_process(
          data_container, context
      )
  
      di, self.len_di = self._flatten_list(data_container.data_inputs)
      eo, self.len_eo = self._flatten_list(
          data_container.expected_outputs
      )
  
      # If is ID and not iterable nested thing, treat them as a special case that replicates the DIT:
      if (
          data_container._ids is not None
          and len(self.len_di) > 0
          and all(
              isinstance(i, (str, int)) for i in data_container._ids
          )
      ):
          # TODO: this code could be put inside the _flatten_list function to avoid duplicating it,
          #           but it would be more complicated to implement as we'd need to consider the len_di
          #           for the ids and eo and add a spare_eo as well.
          self.spare_ids = data_container._ids
          _ids: List[Union[int, str]] = sum(
              [
                  copy.deepcopy([_id] * _count)
                  for _id, _count in zip(self.spare_ids, self.len_di)
              ],
              [],
          )
          self.len_ids = copy.copy(self.len_di)
      else:
          self.spare_ids = None
          _ids, self.len_ids = self._flatten_list(data_container._ids)
  
      flattened_data_container = DACT(
          ids=_ids,
          data_inputs=di,
          expected_outputs=eo,
          sub_data_containers=data_container.sub_data_containers,
      )
  
      self._invariant(data_container, flattened_data_container)
  
      return flattened_data_container, context
  
imports: import copy
  from operator import itemgetter
  from typing import Callable, List, Optional, Tuple, Union
  from neuraxle.base import CX, DACT, BaseStep, BaseTransformer, ForceHandleMixin, ForceHandleOnlyMixin, Identity, MetaStep, NamedStepsList, TruncableJoblibStepSaver
  from neuraxle.data_container import DIT, EOT, IDT, DACTData, ListDataContainer
  from neuraxle.steps.flow import ExecuteIf
  import numpy
label_desc: Use image node_id 2 for calling the FlattenForEach obj's underlying member method code with example usage: obj._will_process(data_container, context) and returns: flattened_data_container,  context