У меня интересный вопрос. Представьте, у нас есть строка вроде:

'i need this {client.car.model} exactly at {client.order.time} today'

Как заменить такие сложные поля значениями объекта? Не знаю, сколько уровней: client.car.door.left ... и т. Д.

Конечно, я могу извлечь это с помощью регулярного выражения в цикле.

Const re = /{(.+?)}/gi;

Пусть regex = re.exec (s);

Но после этого - как заменить все поля и подполя?

1
Sergei Konovalov 18 Май 2021 в 15:18

2 ответа

Лучший ответ

Итак, вы уже правильно определили, КАК получить шаблон, поэтому ваш реальный вопрос: «как я могу взять строку вроде 'client.order.time' и получить для нее значение?»

С рекурсивной функцией это на удивление просто. Скажем, у нас есть объект, как показано ниже:

const data = {
  client: {
    order: {
      time: '12:00PM'
    }
  }
};

Мы можем создать функцию, которая принимает объект и строку и рекурсивно перебирает каждую часть, пока она либо а) не пройдет по всем частям и не найдет значение, либо б) найдет неизвестную часть и не вернет undefined.

const data = {
  client: {
    order: {
      time: '12:00PM'
    }
  }
};

const getValueFromPath = (obj, path, delimiter = '.') => {
  // path or obj are falsy, just return obj
  // you could tweak this bit to do something else if you want
  // obj === undefined might be from our recursive call, so keep that in mind if you tweak
  if (!path || !obj) return obj;

  const parts = path.split(delimiter);
  const next = parts.shift(); // get the first part, parts now has all the other parts
  
  // If !parts.length (parts.length === 0), we're on the last part, so we'll return.
  if (!parts.length) return obj[next];
  
  // Otherwise, recurse with the rest of the parts.
  return getValueFromPath(obj[next], parts.join(delimiter), delimiter);
};

const result = getValueFromPath(data, 'client.order.time');
console.log(result);

const noResult = getValueFromPath(data, 'path.does.not.exist');
console.log(noResult);

Если вам не нравится каждый раз повторно join() вставлять части, у вас может быть вторая версия этого метода, которая принимает массив частей вместо строки path, но я нахожу дополнительные код не стоит (очень минимального) повышения эффективности.

Теперь, возвращаясь к исходному вопросу, просто используйте метод replace() в строке, используя метод replacer в качестве второго аргумента. Второй аргумент этого метода replacer - это шаблон, поэтому мы просто передаем его нашему новому методу.

const str = 
'i need this {client.car.model} exactly at {client.order.time} today';
const data = {
  client: {
    car: {
      model: 'Truck'
    },
    order: {
      time: '12:00PM'
    }
  }
};

const getValueFromPath = (obj, path, delimiter = '.') => {
  // path or obj are falsy, just return obj
  // you could tweak this bit to do something else if you want
  // obj === undefined might be from our recursive call, so keep that in mind if you tweak
  if (!path || !obj) return obj;

  const parts = path.split(delimiter);
  const next = parts.shift(); // get the first part, parts now has all the other parts
  
  // If !parts.length (parts.length === 0), we're on the last part, so we'll return.
  if (!parts.length) return obj[next];
  
  // Otherwise, recurse with the rest of the parts.
  return getValueFromPath(obj[next], parts.join(delimiter), delimiter);
};

const result = str.replace(/{(.+?)}/gi, (_, path) => getValueFromPath(data, path));
console.log(result);
0
samanime 18 Май 2021 в 12:36

Мне просто не нравится мое текущее решение, но оно работает ...

function objReplace(string, object) {
  const re = /{(.+?)}/gi;
  let res = string;
  let regex = re.exec(string);

  while (regex && regex[1]) {
    const ar = regex[1].split('.');
    let obj = object;
    ar.forEach((key) => {
      obj = obj[key];
    });

    obj = obj || '';
    res = res.replace(regex[0], obj);
    regex = re.exec(string);
  }
  return res.replace(/ +/g, ' ');
}
0
Sergei Konovalov 18 Май 2021 в 12:34