Create object from array items - javascript

I'm trying to create the following thing, I couldn't find any success, let me know if there's a way :)
I have 2 variables called text1 and text2, each one of them should represent a key inside an object and the value of the key will be the data variables.
In the beginning, the object will start as empty, and I need to create a function that will create that object inside the object as many times as need, it can be a case where the text will have more than 3 keys, for e.g. items.courses.lessons.assets.title etc.. so the function must be dynamic.
notice that the function must check if the key already exists so it will not overwrite it if there is another text with the same starting keys (see example below)
const data1 = 'hello';
const text1 = 'lessons.first.title';
const data2 = 'hello there';
const text2 = 'lessons.first.description';
// how the end result should look like
const result = {
lessons: {
first: {
title: 'hello',
description: 'hello there',
},
},
};
Thanks! 😁😁

Split the path up, remove the last part. Loop over an object with the remaining path. When you get to the end, set the property wit the value.
function setProp(obj, path, value) {
var parts = path.split(".");
var last = parts.pop();
var current = parts.reduce(function (acc, part) {
acc[part] = acc[part] || {};
return acc[part];
}, obj);
current[last] = value;
}
const data1 = 'hello';
const text1 = 'lessons.first.title';
const data2 = 'hello there';
const text2 = 'lessons.first.description';
var myObj = {};
setProp(myObj, text1, data1 );
setProp(myObj, text2, data2);
console.log(myObj);

Here's how I did it :)
const data1 = 'hello';
const text1 = 'lessons.first.title';
const data2 = 'hello there';
const text2 = 'lessons.first.description';
const res = {};
const addProp = (keys, value) => {
let temp = res;
const len = keys.split('.').length
keys.split('.').forEach((key,index)=>{
if(!temp[key]) {
temp[key] = {};
}
if(index === len - 1){
temp[key] = value;
}
temp = temp[key];
});
}
addProp(text1, data1);
addProp(text2, data2);
console.log(res);

Here is my attempt
function constructObj(obj, path, value) {
const pathArray = path.split('.');
let currentNode = obj;
for (let i = 0; i < pathArray.length; i++) {
const path = pathArray[i];
if (i === pathArray.length - 1) { // if on last element assign the value
currentNode[path] = value;
}
if (currentNode[path] === undefined) { // check if key exists
const newObj = {};
currentNode[path] = newObj;
currentNode = newObj;
} else {
currentNode = currentNode[path];
}
}
}
const result = {};
const data1 = 'hello';
const text1 = 'lessons.first.title';
constructObj(result, text1, data1);
const data2 = 'hello there';
const text2 = 'lessons.first.description';
constructObj(result, text2, data2);

Related

JavaScript: CSV records in to an array of objects with string and number

Hello i am new in JavaScript and i would like to know how to get an array of objects with strings and an array with numbers. Is it possible to use parseInt to convert the array with the numbers in my CSV ?
this is my CSV-Data
word; arrayInt
hello; [1,20,30]
bye; [4,50,60]
this is my output
[{"word":"hello"," arrayInt":" [1,20,30]"},{"word":"bye"," arrayInt":" [4,50,60]"}]
the code i use with the CSV-Data at the top
<head> </head>
<body>
<form id="myForm">
<input type="file" id="csvFile" accept=".csv" />
<br />
<input type="submit" value="Submit" />
</form>
<script>
const myForm = document.getElementById("myForm");
const csvFile = document.getElementById("csvFile");
function csvToArray(str, delimiter = ";") {
// slice from start of text to the first \n index
// use split to create an array from string by delimiter
const headers = str.slice(0, str.indexOf("\n")).split(delimiter);
// slice from \n index + 1 to the end of the text
// use split to create an array of each csv value row
const rows = str.slice(str.indexOf("\n") + 1).split("\n");
// Map the rows
// split values from each row into an array
// use headers.reduce to create an object
// object properties derived from headers:values
// the object passed as an element of the array
const arr = rows.map(function (row) {
const values = row.split(delimiter);
const el = headers.reduce(function (object, header, index) {
object[header] = values[index];
return object;
}, {});
return el;
});
// return the array
return arr;
}
myForm.addEventListener("submit", function (e) {
e.preventDefault();
const input = csvFile.files[0];
const reader = new FileReader();
reader.onload = function (e) {
const text = e.target.result;
const data = csvToArray(text);
var myJSON = JSON.stringify(data);
console.log(myJSON); // Output of the CSV-Data
var tmp = myJSON;
for (var i = 0; i < tmp.length; i++) {
let member = tmp[i];
let arr = JSON.parse(member.arrayInt);
tmp[i].arrayInt = arr;
};
console.log(tmp);
};
reader.readAsText(input);
});
</script>
</body>
the output i want to achieve
[{word:"hello", arrayInt: [1,20,30]},{word:"bye", arrayInt: [4,50,60]}]
What about this?
var tmp = [{"word":"hello","arrayInt":"[1,20,30]"},{"word":"bye","arrayInt":"[4,50,60]"}];
for(var i = 0; i < tmp.length; i++) {
let member = tmp[i];
let arr = JSON.parse(member.arrayInt);
tmp[i].arrayInt = arr;
}
console.log(tmp);
/* outputs
[
{ word: 'hello', arrayInt: [ 1, 20, 30 ] },
{ word: 'bye', arrayInt: [ 4, 50, 60 ] }
]
*/
EDIT
You original code has some issues. You would need to change the following in your code, in order for my suggestion to work.
In the following:
const arr = rows.map(function (row) {
const values = row.split(delimiter);
const el = headers.reduce(function (object, header, index) {
//object[header] = values[index]; // <=== change this
object[header.trim()] = values[index].trim(); // <=== into this
return object;
}, {});
return el;
});
one change is needed in order to clear the spaces you have around the keys and values in your original CSV.
Next, this:
var myJSON = JSON.stringify(data);
console.log(myJSON); // Output of the CSV-Data
var tmp = myJSON;
needs to become like this
//var myJSON = JSON.stringify(data);
//console.log(myJSON); // Output of the CSV-Data
var tmp = data;
We are doing this because there's no need to stringify the data. We need to have it as an array of objects, and we'll work with that.
The rest of your the code stays as it is.

texts in array converts to objects

I am using node to convert an array to object, I have an array looks like this
[
'items[0].book=Book1',
'items[0].color=Red',
'items[0].bookCode=#1',
'items[1].book=Book2',
'items[1].color=Yellow',
'items[1].bookCode=#2',
'items[2].book=Book3',
'items[2].color=Blue',
'items[2].bookCode=#3',
...
]
I am trying to convert it to be objets in one array
items:[
{
book: "Book1",
color: "Red",
bookCode: "#1"
},
{
book: "Book2",
color: "Yellow",
bookCode: "#2"
},
...
]
I found it is easy to conver it uses a 3rd party lib like setKeypath/set,
const obj = {};
const arr = [items......(like above)]
arr.forEach((val => {
if (val.startsWith('items[')) {
const splitWord = item.split('=');
setKeypath(obj, splitWord[0], splitWord[1]);
}
});
I am seeking a way if it can be done the same output with es6, so I don't really need a library. Thanks
const items = [
"items[0].book=Book1",
"items[0].color=Red",
"items[0].bookCode=#1",
"items[1].book=Book2",
"items[1].color=Yellow",
"items[1].bookCode=#2",
"items[2].book=Book3",
"items[2].color=Blue",
"items[2].bookCode=#3"
];
let res = [];
let currId = "";
let currItem = null;
for (let i = 0; i < items.length; i++) {
let parts = items[i].split(".");
if (currId!==parts[0] && currItem) { //new item
res.push(currItem)
currId = parts[0];
}
if (!currItem)
currItem = {};
let keyValue = parts[1].split("=");
currItem[keyValue[0]] = keyValue[1]
}
console.log({items: res})
You may first find all values by regex, and insert the attribute to each corresponding element one by one. This approach works for whatever ordering the array is, and whatever attributes there are, as long as each element follow the same pattern.
let items = [
"items[1].bookCode=#2",
"items[0].book=Book1",
"items[0].bookCode=#1",
"items[1].book=Book2",
"items[2].bookCode=#3",
"items[1].color=Yellow",
"items[2].book=Book3",
"items[2].color=Blue",
"items[0].color=Red",
"items[4].test=test!"
];
let indexPattern = /\[(\d*)\]/;
let attrPattern = /\.(.*)=/;
let valuePattern = /=(.*)/;
let obj = Object.values(
items.reduce((obj, element) => {
let index = element.match(indexPattern)[1];
let attr = element.match(attrPattern)[1];
let value = element.match(valuePattern)[1];
if (!obj.hasOwnProperty(index)) obj[index] = {};
obj[index][attr] = value;
return obj;
}, {})
);
console.log(obj);
[
'items[0].book=Book1',
'items[0].color=Red',
'items[0].bookCode=#1',
'items[1].book=Book2',
'items[1].color=Yellow',
'items[1].bookCode=#2',
'items[2].book=Book3',
'items[2].color=Blue',
'items[2].bookCode=#3',
].reduce((acc, str) => {
const index = Number(str.slice(str.indexOf('[') + 1, str.indexOf(']')));
if (!acc[index]) {
acc[index] = {};
}
const entry = [str.slice(str.indexOf('.') + 1, str.indexOf('=')), str.slice(str.indexOf('=') + 1)];
acc[index][entry[0]] = entry[1];
return acc;
}, []);
Here I pick apart the string you're given based on the consistent format, grab the index, key, and value, and then just use Array#reduce to do the work of putting the array together.
Documentation:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce
I think a smattering of regex would do the trick:
const ar = [
'items[0].book=Book1',
'items[0].color=Red',
'items[0].bookCode=#1',
'items[1].book=Book2',
'items[1].color=Yellow',
'items[1].bookCode=#2',
'items[2].book=Book3',
'items[2].color=Blue',
'items[2].bookCode=#3'
]
const result = [];
ar.forEach(item => {
const index = parseInt(item.match(/\[([0-9]+)\]/)[1]);
const params = item.split(".")[1].split("=");
if(!result[index])
result[index] = {}
result[index][params[0]] = params[1];
})
console.log(result)
Note that item.match(/\[([0-9]+)\]/) matches the number inside your brackets. match returns an array where 1 is the index of the actual value between the brackets.

create nested object from string JavaScript

I have a string like this:
let user = "req.user.role"
is there any way to convert this as nested objects for using in another value like this?
let converted_string = req.user.role
I know I can split the user with user.split(".")
my imagination :
let user = "req.user.role".split(".")
let converted_string = user[0].user[1].user[2]
I found the nearest answer related to my question : Create nested object from query string in Javascript
Try this
let user = "req.user.role";
let userObj = user.split('.').reduceRight((obj, next) => ({
[next]: obj
}), {});
console.log(userObj);
Or this, for old browsers
var user = "req.user.role";
var userArray = user.split('.'), userObj = {}, temp = userObj;
for (var i = 0; i < userArray.length; i++) {
temp = temp[userArray[i]] = {};
}
console.log(userObj);
The function getvalue() will return the nested property of a given global variable:
var user="req.user.role";
var req={user:{role:"admin"}};
function getvalue(str){
return str.split('.').reduce((r,c,i)=>i?r[c]:window[c], '');
}
console.log(getvalue(user));
I'll take my shot at this:
let user = "req.user.role"
const trav = (str, o) => {
const m = str.split('.')
let res = undefined
let i = 0
while (i < m.length) {
res = (res || o)[m[i]]
if (!res) break
i++
}
return res
}
const val = trav(user, {
req: {
user: {
role: "admin"
}
}
})
console.log(val)
this function will traversed the passed in object for the entire length of the provided string.split "." list returning either a value or undefined.
You can do it like this:
let userSplitted = "req.user.role".split('.');
let obj, o = obj = {};
userSplitted.forEach(key=>{o=o[key]={}});

Convert string to JSON stringformat

I have a string where the object name is separated by dot from the field name as follows:
{\"person.firstName\":\"Ahmed\",\"person.job\":\"Doctor\",\"products.packName\":\"antibiotic\",\"products.packSize\":\"large\"}}";
I want to parse it to look like the json format:
"{\"person\": {\"firstName\":\"Ahmed\",\"job\":\"Doctor\",},\"products\": {\"packName\":\"antibiotic\",\"packSize\":\"large\"}}";
Is there an efficient algorithm for that?
Maybe this is help for you.
var str='{\"person.firstName\":\"Ahmed\",\"person.job\":\"Doctor\",\"products.packName\":\"antibiotic\",\"products.packSize\":\"large\"}';
var newObj={}
var json=JSON.parse(str);
for (i in json) {
var splt=i.split('.');
var key=splt[0];
var subkey=splt[1];
if(!(key in newObj)) {
newObj[key]={};
}
newObj[key][subkey]=json[i];
}
//if you need string use str or not use newObj
var str=JSON.stringify(newObj);
console.log(str);
console.log(newObj);
console.log(newObj.person);
console.log(newObj.person.firstName);
console.log(newObj.person.job);
Assuming left side may have multiple dots
let parsedObj = JSON.parse(<..YOUR STRINGIFIED JSON..>);
let myObj = {};
Object.keys(parsedObj).forEach((key) => {
let val = parsedObj[key];
let subKeys = key.split(".");
let currentRef = myObj;
subKeys.forEach((subKey, idx) => {
if(idx === subKeys.length - 1) {
currentRef[subKey] = val;
} else {
currentRef[subKey] = currentRef[subKey] || {};
currentRef = currentRef[subKey];
}
});
});
let finalString = JSON.stringify(myObj);
P.S. Your string seems to contain an extra } in the end

typescript : logic to covert string array into custom object

Here is my requirement. I was able to achieve to some level in java but we need to move it to typescript (client side).
Note: The below input is for example purpose and may vary dynamically.
Input
var input = ["a.name", "a.type", "b.city.name" , "b.city.zip", "b.desc","c"];
We need to create an utility function that takes above input and returns output as below.
Output:
Should be string not an object or anything else.
"{ a { name, type }, b { city {name, zip } , desc }, c }"
any help is much appreciated.
I don't see that typescript plays any role in your question, but here's a solution for constructing the string you requested. I first turn the array into an object with those properties, then have a function which can turn an object into a string formatted like you have
const input = ["a.name", "a.type", "b.city.name" , "b.city.zip", "b.desc","c"];
const arrayToObject = (arr) => {
return arr.reduce((result, val) => {
const path = val.split('.');
let obj = result;
path.forEach(key => {
obj[key] = obj[key] || {};
obj = obj[key];
});
return result;
}, {});
}
const objectToString = (obj, name = '') => {
const keys = Object.keys(obj);
if (keys.length === 0) {
return name;
}
return `${name} { ${keys.map(k => objectToString(obj[k], k)).join(', ')} }`;
}
const arrayToString = arr => objectToString(arrayToObject(arr));
console.log(arrayToString(input));
Here's another variation. Trick is to parse the strings recursively and store the intermediate results in an Object.
function dotStringToObject(remainder, parent) {
if (remainder.indexOf('.') === -1) {
return parent[remainder] = true
} else {
var subs = remainder.split('.');
dotStringToObject(subs.slice(1).join('.'), (parent[subs[0]] || (parent[subs[0]] = {})))
}
}
var output = {};
["a.name", "a.type", "b.city.name" , "b.city.zip", "b.desc","c"].forEach(function(entry) {
dotStringToObject(entry, output)
});
var res = JSON.stringify(output).replace(/\"/gi, ' ').replace(/\:|true/gi, '').replace(/\s,\s/gi, ', ');
console.log(res)
// Prints: { a { name, type }, b { city { name, zip }, desc }, c }
You could do something like this:
var input = ["a.name", "a.type", "b.city.name" , "b.city.zip", "b.desc","c"];
var output = {};
for(var i =0; i < input.length; i+=2){
output[String.fromCharCode(i+97)] = {};
output[String.fromCharCode(i+97)].name = input[i];
output[String.fromCharCode(i+97)].type = input[i+1];
}
console.log(JSON.stringify(output));

Categories