0

I have a CSV file below and I want to build a javascript object out of it.

I am trying to extract the elements from the "category" column and create a hierarchy object then put information under the "color" column in the lowest level of the object.

Goal:

enter image description here

Below is what I have attempted so far to extract the information from the CSV and what sure how to proceed from here. What is the best and most efficient?

let csv = `id, category, year, color, price \n
01, SUV > AWD > Sport > Benz, 2017, blue, $20 \n
02, SUV > FWD > Family > Benz, 2018, black, $20 \n
03, Sedan > AWD > BNW, 2017, white, $30 \n
04, SUV > AWD > Sport > BNW, 2012, red, $30 \n
05, Seden > RWD > Toyota, 2019, white, $30`

function extractCSV(csv){
    let object = {}
    let data = csv.split('\n')

    for(let i = 1; i < data.length; i++){
        let subData = data[i].split(',')
        for(let j = 1; j < subData.length; j++){
            let header = subData[j].split(">")

            if(header.length > 1){
                for(let k = 0; k < header.length; k++){
                    
                }
            } 
        }
    }
    return object
}

Thanks in advance

1 Answer 1

1

First, the "better" of the two answers.

You'll see it follows mostly the same flow as you started with, however, I chose to put all the parsing up front, into some intermediate objects. This allows the latter code to focus on just building our tree.

let csv = `id, category, year, color, price \n
01, SUV > AWD > Sport > Benz, 2017, blue, $20 \n
02, SUV > FWD > Family > Benz, 2018, black, $20 \n
03, Sedan > AWD > BNW, 2017, white, $30 \n
04, SUV > AWD > Sport > BNW, 2012, red, $30 \n
05, Sedan > RWD > Toyota, 2019, white, $30`;

function parseCsv(csv) {
  let object = {};

  // Parse/Sanitize all the data up front
  const rows = csv
    .split("\n")
    .filter((row, index) => index && row)
    .map((row) => {
      const columns = row.split(",");
      return {
        headers: columns[1].split(">").map((p) => p.trim()),
        color: columns[3].trim()
      };
    });

  // Then spin through the rows
  for (const row of rows) {
    let currentObj = object;

    // Then spin through the headers
    for (let hi = 0; hi < row.headers.length; ++hi) {
      const header = row.headers[hi];

      // If it is the last header, assign the color to it
      if (hi === row.headers.length - 1) {
        currentObj[header] = row.color;
        break;
      }

      // Else we need to get or set another object level
      currentObj = currentObj[header] = currentObj[header] ?? {};
    }
  }

  return object;
}

console.log(parseCsv(csv));

Second, I took another approach here. I find some elegance in a recursive solution. Although, it is probably harder to read, will most likely utilize more memory, and be slower, especially has the document/header depth grows. But I included it for fun!

let csv = `id, category, year, color, price \n
01, SUV > AWD > Sport > Benz, 2017, blue, $20 \n
02, SUV > FWD > Family > Benz, 2018, black, $20 \n
03, Sedan > AWD > BNW, 2017, white, $30 \n
04, SUV > AWD > Sport > BNW, 2012, red, $30 \n
05, Sedan > RWD > Toyota, 2019, white, $30`;

const parseCsv = (csv) => {
  const result = {};

  // Parse/Sanitize all the data up front
  const rows = csv
    .split("\n")
    .filter((row, index) => index && row)
    .map((row) => {
      const columns = row.split(",");
      return {
        headers: columns[1].split('>').map(p => p.trim()),
        color: columns[3].trim()
      };
    });

  // The recursive function, takes in
  // {target} the current object in the tree we're  at
  // {headers} the remaining headers to step through
  // {color} the color to assign to the last header
  const handleRow = (target, headers, color) => {
    // Last header, so just assign the color to it
    if (headers.length === 1) {
      target[headers[0]] = color;
      return;
    }

    // Else we need to get or set another object level
    const newTarget = (target[headers[0]] = target[headers[0]] ?? {});
    // And call into the next level with it and the remaining headers
    handleRow(newTarget, headers.slice(1), color);
  };

  for (const row of rows) {
    handleRow(result, row.headers, row.color);
  }

  return result;
};

console.log(parseCsv(csv));
Sign up to request clarification or add additional context in comments.

2 Comments

Thank you so much for your code above, it achieved exactly what I am looking for. If I may ask some questions, in your code you never touched "object" but instead used "currentObj" but at the end "object" has all the information added, what was the reason behind that?
At the very beginning of processing each row, we reset the currentObj equal to the object, we then modify it via that reference. As far as the reason behind having a currentObj, it is just a variable to keep track of current object we are dealing with as walking/growing the tree.

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.