0

I'm trying to stringfy an object with my custom function, I just do it for practice purposes. But the function doesn't return what I want.

Here is the whole function and the object I pass into.

const data = { hello: "world", is: true, nested: { count: 5 } };

const stringify = (obj, symbol, numRepeat, stringifiedObj) => {
  const keys = Object.keys(obj);

  const values = Object.values(obj);
  // console.log("stringified object when functino runs", stringifiedObj);

  // console.log("ALL KEYS", keys);
  // console.log("ALL VALUES", values);

  keys.forEach((key, index) => {
    // console.log(typeof obj[key]);

    if (typeof values[index] !== "object") {
      console.log(values[index]);
      // console.log(`{${key}: ${values[index]}}`);

      stringifiedObj += `${key}: ${values[index]}\n`;

      console.log("strinbgify inside if".toLocaleUpperCase(), stringifiedObj);
    } else {
      console.log("this is Object", obj[key]);
      stringify(obj[key], symbol, values, stringifiedObj);
    }
  });

  return `{${stringifiedObj}}`;
};

console.log("FUNCTION RETURN", stringify(data, "|-", 2, ""));


You can ignore symbol and numrepeat parameters since I will use them later.

so the expected result is

hello: world
is: true
nested:{
count: 5 
}

but it returns;

hello: world
is: true

where am I doing wrong?

2
  • 1
    A couple problems: stringify returns something, but you completely ignore the return value in the recursive call. But also, you use forEach which ignores return values in general, so even a return stringify() won’t fix it. You should use a different Array method. Commented Sep 12, 2021 at 17:37
  • The main problem is that you're reinitializing stringifiedObj in this call: stringifiedObj += ${key}: ${values[index]}\n; Inside a single call that's not a problem, but in the recursive call, it is ignored because you don't return and use that value. Commented Sep 13, 2021 at 0:58

2 Answers 2

2

This should be a fixed version. I added some comments in the code.

Basically with recursion you usually need to return a value that will be grabbed in the nested iteration.

Array.ForEach does no return anything, just executes code of each item. Array.map instead, returns the result.

const data = { hello: "world", is: true, nested: { count: 5 } };

const stringify = (obj, symbol, numRepeat, stringifiedObj) => {
  const keys = Object.keys(obj);

  const values = Object.values(obj);

  // Here you need to RETURN something, so array.map maps an array into something else and you can return a value
  return keys.map((key, index) => {
    if (typeof values[index] !== "object") {

      // so return here the .map iteration
      return stringifiedObj + `${key}: ${values[index]}\n`; // You really need that \n at the end?
    } else {
      // else, return your other string
      return stringify(obj[key], symbol, values, stringifiedObj);
    }
  });

  // as we did -return keys.map- this one below is no longer needed
  //return stringifiedObj;
};

console.log("FUNCTION RETURN", stringify(data, "|-", 2, ""));

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

2 Comments

this is an improvement okay but it gives me an array, I also want to show the nested objects as a string like I wrote on "expected result" in the question, basically I want a whole string that looks like an object
Do you necessarily want to write a custom function? Because otherwise JSON.stringify(data) is exactly what you need :)
1

You might like this simple type analysis technique using switch on t?.constructor. Each case performs only what is necessary for each type's "shape" and recursively call stringify on the type's child elements. By adding a default depth = 1 parameter, we are able to format the output using properly indented lines =

function stringify (t, depth = 1) {
  switch (t?.constructor) {
    case String:
      return `"${t.replace(/"/g, '\\"')}"`
    case Object:
      return `{${linebreak(depth)}${
        Object
          .entries(t)
          .map(([k,v]) => `${k}: ${stringify(v, depth + 1)}`)
          .join(`,${linebreak(depth)}`)
      }${linebreak(depth - 1)}}`
    case Array:
      return `[${linebreak(depth)}${
        t
          .map(v => stringify(v, depth + 1))
          .join(`,\n${"  ".repeat(depth)}`)
      }${linebreak(depth - 1)}]`
    default:
      return String(t)
  }
}

function linebreak (depth, s = "  ") {
  return `\n${s.repeat(depth)}`
}

const data = 
  { hello: "world", is: true, nested: { count: 5, any: null, with: [ "array", undefined, { object: NaN } ] } }

console.log(stringify(data))


{
  hello: "world",
  is: true,
  nested: {
    count: 5,
    any: null,
    with: [
      "array",
      undefined,
      {
        object: NaN
      }
    ]
  }
}

1 Comment

Damn, just about to post the same answer in a slightly different style! Beautiful as always

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.