content: package com.annimon.ownlang.modules.functional;
  
  import com.annimon.ownlang.exceptions.TypeException;
  import com.annimon.ownlang.lib.Arguments;
  import com.annimon.ownlang.lib.ArrayValue;
  import com.annimon.ownlang.lib.Function;
  import com.annimon.ownlang.lib.MapValue;
  import com.annimon.ownlang.lib.Types;
  import com.annimon.ownlang.lib.Value;
  import com.annimon.ownlang.lib.ValueUtils;
  import java.util.Map;
  
  final class functional_map implements Function {
  
      @Override
      public Value execute(Value[] args) {
          Arguments.checkOrOr(2, 3, args.length);
          
          final Value container = args[0];
          if (container.type() == Types.ARRAY) {
              final Function mapper = ValueUtils.consumeFunction(args[1], 1);
              return mapArray((ArrayValue) container, mapper);
          }
          
          if (container.type() == Types.MAP) {
              final Function keyMapper = ValueUtils.consumeFunction(args[1], 1);
              final Function valueMapper = ValueUtils.consumeFunction(args[2], 2);
              return mapMap((MapValue) container, keyMapper, valueMapper);
          }
  
          throw new TypeException("Invalid first argument. Array or map expected");
      }
      
      static ArrayValue mapArray(ArrayValue array, Function mapper) {
          final int size = array.size();
          final ArrayValue result = new ArrayValue(size);
          for (int i = 0; i < size; i++) {
              result.set(i, mapper.execute(array.get(i)));
          }
          return result;
      }
      
      static MapValue mapMap(MapValue map, Function keyMapper, Function valueMapper) {
          final MapValue result = new MapValue(map.size());
          for (Map.Entry<Value, Value> element : map) {
              final Value newKey = keyMapper.execute(element.getKey());
              final Value newValue = valueMapper.execute(element.getValue());
              result.set(newKey, newValue);
          }
          return result;
      }
  }
  