Return the shortest substring containing all the characters in the set The task:


  Given a string and a set of characters, return the shortest substring
  containing all the characters in the set.
  
  For example, given the string "figehaeci" and the set of characters
  {a, e, i}, you should return "aeci".
  
  If there is no substring containing all the characters in the set,
  return null.


My solution:

// init
const originalStr = 'figehaeci';
const set = Object.freeze(['a', 'e', 'i']);

// helpers
const isEmpty = s =&gt; s.length === 0;
const copy = s =&gt; s.slice(0);
const getNextResult = (currRes, endRes) =&gt; {
    if (!endRes || isEmpty(endRes)) {
      return currRes;
    }
    return currRes.length &lt; endRes.length ?
           currRes : endRes;
};

// main
const contains = (str, endResult) =&gt; {
  const currStr = str;
  const currSet = copy(set);
  let pushIt;
  const currResult = currStr
    .split('')
    .reduce((acc, item) =&gt; {
      if (pushIt === false) {
        return acc;
      }
      if (currSet.includes(item)) {
        currSet.splice(currSet.indexOf(item), 1);
        pushIt = true;
      }
      if (pushIt) {
        acc.push(item);
      }
      if (isEmpty(currSet)) {
        pushIt = false;
      }
      return acc;
    }, []);

  const nextStr = str
    .split('')
    .slice(1)
    .join('');

  // exit case
  if (isEmpty(nextStr)) {
    return endResult ? endResult.join('') : null;
  }

  // recursive case
  const nextResult = isEmpty(currSet) ?
        getNextResult(currResult, endResult) :
        endResult;

  return contains(nextStr, nextResult);
}

// result
console.log(contains(originalStr, null));


I don't like the many ifs and the flag (pushIt). Do you know how to get rid of them without sacrificing readability and maintainability? 

Doesn't have to be in functional programming style. Just looking for an elegant solution.
I couldn't remove the ifs and the flag , so i thought about a diffrent aproach, 

Using regular expressions seem more reasonable aproach, you can have a function that returns all the possible combinations of the letters in the array, create regular expressions from the resulting combinations and match against the orignalStr, 



function perm(xs) {
  let ret = [];
  for (let i = 0; i &lt; xs.length; i = i + 1) {
    let rest = perm(xs.slice(0, i).concat(xs.slice(i + 1)));

    if (!rest.length)
      ret.push([xs[i]]);
    else
      for (let j = 0; j &lt; rest.length; j = j + 1)
        ret.push([xs[i]].concat(rest[j]));
  }
  return ret;
}

const originalStr = 'figehaeci', letters = ['a', 'e', 'i'];
let result = originalStr;

const combinations = perm(letters).map(combo =&gt; combo.join('.*?'));

// combinations : ["a.*?e.*?i","a.*?i.*?e","e.*?a.*?i","e.*?i.*?a","i.*?a.*?e","i.*?e.*?a"]

combinations.forEach(combo =&gt; {
  const exp = new RegExp(combo, 'g');
  const matches = originalStr.match(exp) || [];

  matches.forEach(match =&gt; {
    if (match.length &lt;= result.length)
      result = match;
  });
});

console.log(result);




The code you posted returns only one value if there's more substrings containing those letters, here's a tweak to store all possible shortest combinations in an array :



function perm(xs) {
  let ret = [];
  for (let i = 0; i &lt; xs.length; i = i + 1) {
    let rest = perm(xs.slice(0, i).concat(xs.slice(i + 1)));

    if (!rest.length)
      ret.push([xs[i]]);
    else
      for (let j = 0; j &lt; rest.length; j = j + 1)
        ret.push([xs[i]].concat(rest[j]));
  }
  return ret;
}

const originalStr = 'figehaecizaexi', letters = ['a', 'e', 'i'];
let shortestLength = originalStr.length, result = [];

const combinations = perm(letters).map(combo =&gt; combo.join('.*?'));

// combinations : ["a.*?e.*?i","a.*?i.*?e","e.*?a.*?i","e.*?i.*?a","i.*?a.*?e","i.*?e.*?a"]

combinations.forEach(combo =&gt; {
  const exp = new RegExp(combo, 'g');
  const matches = originalStr.match(exp) || [];

  matches.forEach(match =&gt; {
    if (match.length &lt; shortestLength) {
      shortestLength = match.length;
      result = [match];
    }
    if (match.length === shortestLength) {
      result.push(match);
    }
  });
});

const deduped = [...new Set(result)];

console.log(deduped);