1

I have a plainText object. I also have an array of objects. In that array of objects, each object contains offset and length keys.

I want to loop over my plainText string, insert the proper words into the string at certain offsets and then return that string.

Here is my code below

const plainText = "Hey name your food is ready, , your food has your name on it"

expected output below

mergeValues = [
{message: "Hey Keith your salmon is ready, your food has your name on it"},
{message: "Hey Kelly your pizza is ready, your food has your name on it"},
{message: "Hey Ed your hamburger is ready, your food has your name on it"},
{message: "Hey Shelby your sushi is ready, your food has your name on it"}
]
const people = [
{ name: "keith", food: "salmon"},
{ name: "kelly", food: "pizza"},
{ name: "ed", food: "hamburger"},
{ name: "shelby", food: "sushi"}
]
const locations = [
 {offset: 4, length: 4, field: 'name'},
 {offset: 13, length: 4, field: 'food'}
]

Here I am mapping over people, creating an object, then running a forEach on locations, and finally returning that object back to the map to let it run again on the next person in people array. Im pretty sure my main issue is that I am Rewriting over the object everytime in the forEach loop instead of modifying the string, saving that value, then modifying that string again, saving value etc.....

const mergeValues = people.map((person) => { 
    const messageObj = {message: ""};
    locations.forEach((location) => { 
        if(Object.keys(person).includes(location.field)) { 
            messageObj.message = plainText.substring(0, location.offset + 1) + person[location.field] + plainText.substring(location.length + location.offset + 1, plainText.length)
        } 
    }) 
return messageObj

6
  • if(Object.keys(person).includes(location)) - this statement will always be false because Object.keys(person) is array of strings and it will never contain location because it is object. You probably want to query if(Object.keys(person).includes(location.field)) Commented Mar 29, 2022 at 7:36
  • I switched it to location.field still only getting the last field needed though... Is there a way to make copies of the object while iterating? Im trying to spread but no luck Commented Mar 29, 2022 at 7:47
  • Wouldn't .replace do the same job? Or do you have to use provided offset etc? Commented Mar 29, 2022 at 7:52
  • 1
    You're not adding to messageObj.message in your forEach, but overwriting it from the original plainText so it will only ever reflect the last match found. You should be initializing message with plainText and then mutating it, but keep in mind that the offsets won't match after the first mutation Commented Mar 29, 2022 at 7:52
  • @pilchard my thoughts aswell, Im trying to find a way to mutate while keeping track of offsets... fun times Commented Mar 29, 2022 at 8:23

4 Answers 4

2

If you can change the plainText string to use custom vars, you can create your own function to fill those vars using regular expressions

const template =
  "Hey {name} your {food} is ready, your food has your name on it";

const variables = [
  { name: "keith", food: "salmon" },
  { name: "kelly", food: "pizza" },
  { name: "ed", food: "hamburger" },
  { name: "shelby", food: "sushi" }
];

function renderTemplate(template, variables) {
  let result = template;

  for (let [key, value] of Object.entries(variables)) {
    result = result.replace(new RegExp(`{${key}}`, "g"), value);
  }

  return result;
}

const results = variables.map((variableValues) =>
  renderTemplate(template, variableValues)
);

console.log(results);

With this approach you can even put the same variable in multiple places, you control the variable location using your own syntaxs ({varName} in this case).

Less complexity, you don't need to count positions, that approach will make adding new vars harder.

const template = 
  "Hey {name} your {food} is ready, your food has your name on it - {name}"

Will compile to

"Hey Keith your salmon is ready, your food has your name on it - Keith"

just adding {name} to the template

CodeSandbox: https://codesandbox.io/s/loving-cherry-jnwt5s?file=/src/index.js

Sign up to request clarification or add additional context in comments.

1 Comment

This is crazy, I was laying in bed after trying to figure this out for like 4 hours, and this scenerio popped in my head. What if I wrap the fields I need to change in some sort of character like a curly brace and then just run the replace on fields only wrapped in curlys. Thank you for this
0

Your solution is pretty complex, try with String.replace, like this:

    const mergeValues = people.map((person) => { 
        const messageObj = {message: plainText};
        for(const key in person){
            messageObj.message = messageObj.message.replace(key, person[key])
        }
    return messageObj
})

Comments

0

You can simply use map, reduce and replace to achieve the result as:

const plainText = 'Hey name your food is ready';

const people = [
  { name: 'keith', food: 'salmon' },
  { name: 'kelly', food: 'pizza' },
  { name: 'ed', food: 'hamburger' },
  { name: 'shelby', food: 'sushi' },
];
const locations = [
  { offset: 4, length: 4, field: 'name' },
  { offset: 14, length: 4, field: 'food' },
];

const mergeValues = people.map((person) => {
  return locations.reduce((acc, { field }) => {
    return acc.replace(field, person[field]);
  }, plainText);
});

console.log(mergeValues);

Comments

0

You can try this with replace.

const plainText = "Hey name your food is ready, your food has your name on it"

const people = [
{ name: "keith", food: "salmon"},
{ name: "kelly", food: "pizza"},
{ name: "ed", food: "hamburger"},
{ name: "shelby", food: "sushi"}
]

const locations = [
 {offset: 4, length: 4, field: 'name'},
 {offset: 14, length: 4, field: 'food'}
]
    
const mergeValues = people.map((person) => { 
    let plainTextLength = plainText.length;
    let messageObj = {message:plainText};
    locations.map((location) => { 
        let lengthOffset = 0;
        if(messageObj.message.length > plainTextLength){
            lengthOffset = messageObj.message.length - plainTextLength;
            messageObj.message = messageObj.message.replace(messageObj.message.substring(location.offset + lengthOffset, location.offset + location.length + lengthOffset), person[location.field]);
        } else if (messageObj.message.length < plainTextLength) {
             lengthOffset = plainTextLength - messageObj.message.length;
            messageObj.message = messageObj.message.replace(messageObj.message.substring(location.offset - lengthOffset, location.offset + location.length - lengthOffset), person[location.field]);
        } else {
             messageObj.message = messageObj.message.replace(messageObj.message.substring(location.offset, location.offset + location.length), person[location.field]);
        }
    })
return messageObj
});

console.log(mergeValues);

3 Comments

I like this answer, It works for the original plainText object I provided, but if I add the word "name" or "food" into the plainText string now it doesnt works as expected. I need to somehow mutate the messageObj.message while keeping track of the offsets. fun times
I have edited the code, I think this will resolve your issue.
Hey Tabish, I really like this answer. Look at Link's answer, He did an interesting approach with regex. Im not sure which answer to mark as correct since both your answer and his get the task done. I will play with both and mark answer soon.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.