2

I am trying to convert an array that looks like the one below, into the structure of the object below the array.

I have tried a number of things, which I will list below as well. But I feel I am going about it the wrong way. And would like some assistance with this.

var array = [
  ["plan_a", "tester_a", "product", "foo", "blocked", "5"],
  ["plan_a", "tester_a", "product", "foo", "passed", "10"],
  ["plan_a", "tester_a", "subproduct", "client", "passed", "15"],
  ["plan_a", "tester_b", "product", "bar", "blocked", "5"],
  ["plan_a", "tester_b", "product", "bar", "passed", "10"],
  ["plan_a", "tester_b", "subproduct", "server", "failed", "5"],
  ["plan_a", "tester_b", "subproduct", "server", "passed", "5"],
  ["plan_a", "tester_c", "product", "foo", "failed", "5"]
];

var object = {
  plan_a: {
    tester_a: {
      product: {
        product: "foo",
        blocked: "5",
        passed: "10"
      },
      subproduct: {
        subproduct: "client",
        passed: "15"
      }
    },
    tester_b: {
      product: {
        product: "bar",
        blocked: "5",
        passed: "10"
      },
      subproduct: {
        subproduct: "server",
        failed: "5",
        passed: "5"
      }
    },
    tester_c: {
      product: {
        product: "foo",
        failed: "5"
      }
    }
  }
};

I have tried this:

var object = {}
var combining_plans =[]
var first_row = arr[0][0];

for (var i = 0; i < arr.length; i++) {
    if (arr[i][0] == first_row) {
        object[arr[i][0]] = [];
        arr[i].shift();
        combining_plans.push(arr[i]);
    }
}

object[first_row] = combining_plans;

This checks the first column for the same if the same it takes the first element turns it into the first key of the object and then adds the values as all the remaining elements of the array.

I was thinking about doing that over and over for every column, but I feel that may not work, and there is a more elegant way than to do this.

3 Answers 3

3

const array = [
	["plan_a", "tester_a", "product"   ,"foo"   ,"blocked", "5"  ],
	["plan_a", "tester_a", "product"   ,"foo"   ,"passed" , "10" ],
	["plan_a", "tester_a", "subproduct","client","passed" , "15" ],
	["plan_a", "tester_b", "product"   ,"bar"   ,"blocked", "5"  ],
	["plan_a", "tester_b", "product"   ,"bar"   ,"passed" , "10" ],
	["plan_a", "tester_b", "subproduct","server","failed" , "5"  ],
	["plan_a", "tester_b", "subproduct","server","passed" , "5"  ],
	["plan_a", "tester_c", "product"   ,"foo"   ,"failed" , "5"  ],
];
const result = {};
array.forEach((item) => {
	const [plan, tester, type, name, key, value] = item;
	if(!result[plan]) {
		result[plan] = {};
	}
	if(!result[plan][tester]) {
		result[plan][tester] = {};
	}
	if(!result[plan][tester][type]) {
		result[plan][tester][type] = {[type]: name};
	}
	Object.assign(result[plan][tester][type], {[key]: value});
});
console.log(result);

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

Comments

0

You can use Array.reduce(), and Array.forEach() to create the object:

const array = [
  ["plan_a", "tester_a", "product"   ,"foo"   ,"blocked", "5"   ],
  ["plan_a", "tester_a", "product"   ,"foo"   ,"passed" , "10"  ],
  ["plan_a", "tester_a", "subproduct","client","passed" , "15"  ],
  ["plan_a", "tester_b", "product"   ,"bar"   ,"blocked", "5"   ],
  ["plan_a", "tester_b", "product"   ,"bar"   ,"passed" , "10"  ],
  ["plan_a", "tester_b", "subproduct","server","failed" , "5"  ],
  ["plan_a", "tester_b", "subproduct","server","passed" , "5"  ],
  ["plan_a", "tester_c", "product"   ,"foo"   ,"failed" , "5"  ],
];

const result = array.reduce((r, row) => {
  const val = row[row.length - 1];
  const path = row.slice(0, -1);
  let current = r;
  
  path.forEach((p, i) => {
    if(i === 4) current[p] = val;
    else if(i === 3) current[path[2]] = p;
    else {
      current[p] = current[p] || {};
      current = current[p];
    }
  });
  

  return r;
}, {});

console.log(result);

Comments

0

An alternative to provided answers would be to map+reduce.

You can map your array into object-chunks of result-like shapes which you can deep merge after:

const array = [
  ["plan_a", "tester_a", "product", "foo", "blocked", "5"],
  ["plan_a", "tester_a", "product", "foo", "passed", "10"],
  ["plan_a", "tester_a", "subproduct", "client", "passed", "15"],
  ["plan_a", "tester_b", "product", "bar", "blocked", "5"],
  ["plan_a", "tester_b", "product", "bar", "passed", "10"],
  ["plan_a", "tester_b", "subproduct", "server", "failed", "5"],
  ["plan_a", "tester_b", "subproduct", "server", "passed", "5"],
  ["plan_a", "tester_c", "product", "foo", "failed", "5"]
];


const result = array.map(mapIntoResultChunkShape).reduce(merge);
//                                                       ^^^^^ comes from lodash

function mapIntoResultChunkShape([
  product,
  tester,
  categoryName,
  categoryValue,
  resultName,
  resultValue
]) {
  return {
    [product]: {
      [tester]: {
        [categoryName]: {
          [categoryName]: categoryValue,
          [resultName]: resultValue
        }
      }
    }
  };
}

console.log(JSON.stringify(result, null, 2));
<script src="https://unpkg.com/[email protected]/index.js"></script>

The main benefit of this approach is that it decouples concerns. To adjust this code to any other array->object transformation, all you'd need to do is change the way an array item maps into a result shape i.e. the mapIntoResultChunkShape function.

You could also build your own merge function specialized for your use-case which can carry less overhead than using a more-generalized library that is built to handle all cases:

const array = [
  ["plan_a", "tester_a", "product", "foo", "blocked", "5"],
  ["plan_a", "tester_a", "product", "foo", "passed", "10"],
  ["plan_a", "tester_a", "subproduct", "client", "passed", "15"],
  ["plan_a", "tester_b", "product", "bar", "blocked", "5"],
  ["plan_a", "tester_b", "product", "bar", "passed", "10"],
  ["plan_a", "tester_b", "subproduct", "server", "failed", "5"],
  ["plan_a", "tester_b", "subproduct", "server", "passed", "5"],
  ["plan_a", "tester_c", "product", "foo", "failed", "5"]
];

const result = array.map(mapIntoResultChunkShape).reduce(mergeResultChunkShapes);

function mapIntoResultChunkShape([
  product,
  tester,
  categoryName,
  categoryValue,
  resultName,
  resultValue
]) {
  return {
    [product]: {
      [tester]: {
        [categoryName]: {
          [categoryName]: categoryValue,
          [resultName]: resultValue
        }
      }
    }
  };
}

function mergeResultChunkShapes(item1, item2) {
  if (
    item1 === null ||
    typeof item1 !== "object" ||
    (item2 === null || typeof item2 !== "object")
  ) {
    return item1 || item2;
  }

  const uniqueKeys = new Set(Object.keys(item1).concat(Object.keys(item2)));

  return Array.from(uniqueKeys).reduce((res, key) => {
    return Object.assign(res, {
      [key]: mergeResultChunkShapes(item1[key], item2[key])
    });
  }, {});
}

console.log(JSON.stringify(result, null, 2));

Comments

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.