JavaScript Read data from multidimensional object with dynamic keys [duplicate] - javascript

This question already has answers here:
Accessing nested JavaScript objects and arrays by string path
(44 answers)
Closed 21 days ago.
Background
I have an object like below:
// Data from the database according to their columns and subordinate tables.
const itemData = {
'license_number': '25/2022',
'application': {
'tracking_number': '535345345',
'applicant_name': 'Misir Ali Humayun'
}
};
I know I can read the data from this object like below:
console.log(itemData.licese_number); // '25/2022'
console.log(itemData.application.tracking_number); // '535345345'
// or
console.log(itemData["licese_number"]); // '25/2022'
console.log(itemData.["application"]["tracking_number"]); // '535345345'
Actual Issue
I want to read the data in a dynamic fashion. For example, I have a defined structure like below:
var placeholder = [
{
"find": "%LICENSE%",
"column": "license_number"
},
{
"find": "%APPLICANT%",
"column": "application.applicant_name"
}
];
Now if I have a replacement function:
const replacePlaceholdersWithData = (content) => {
var replacements = {};
// Constructing the object like { "%STRING%" : "DB_VALUE", "%STRING_2%" : "DB_VALUE_2" }
placeholders.filter((object) => replacements[object.find] = itemData[object.column]); // 👈 ISSUE HERE
// #props https://stackoverflow.com/a/7975025/1743124
content = content.replace(/%\w+%/g, function (all) {
return replacements[all] || all;
});
return content;
};
...and a content like below:
const content = "A License numbered %LICENSE% from the %APPLICANT% is received";
console.log(replacePlaceholdersWithData(content));
// Will output: 'A License numbered 25/2022 from the %APPLICANT% is received'
You can guess that the first placeholder (%LICENSE%) will be replaced with '25/2022', but the second one won't be affected, because of the reading style at 👈: itemData["application.applicant_name"] won't match with any real column/key.
QUESTION: How can I resolve that issue?
I know I can use object.column.includes('.') along with object.column.split('.') and get an array of dimensions. But I am clueless, how can I use the array to get data from a multi-dimensional object dynamically?

const itemData = {
'license_number': '25/2022',
'application': {
'tracking_number': '535345345',
'applicant_name': 'Misir Ali Humayun'
}
};
const placeholder = [
{
"find": "%LICENSE%",
"column": "license_number"
},
{
"find": "%APPLICANT%",
"column": "application.applicant_name"
}
];
const content = "A License numbered %LICENSE% from the %APPLICANT% is received";
function replacePlaceholdersWithData(data, content) {
placeholder.forEach(({find, column})=>content = content.replaceAll(find, lookup(data, column)))
return content;
}
function lookup(data, path) {
let [first, ...rest] = path.split('.');
return rest.length ? lookup(data[first], rest.join('.')) : data[path];
}
console.log(replacePlaceholdersWithData(itemData, content));

Related

Converting CSV to nested JSON in Javascript some data in multiple rows

I have a CSV file which needs to be converted into a Javascript object / JSON file. Doesn't really matter which since I'll be be handling the data in JS anyway and either is fine.
Data in csv:-
Name,Time,HeightOptions/WidthRange,HeightOptions/Options
EGBL,Today,12,12.13.14.15.16
,,26,12.13.14.15.16
Desired Output:-
{
Name:"EGBL",
Time:"Today",
HeightOptions : [
{WidthRange:"12",
Options:[12,13,14,15,16]},
{WidthRange:"26",
Options:[12,13,14,15,16]
}]
}
This is what I have came up with:
const CSV = (csv) => {
var attrs = csv.splice(0, 1);
console.log("attrsattrs", attrs);
var result = csv.map(function (row) {
var obj = {};
var rowData = row.split(",");
attrs[0].split(",").forEach(function (val, idx) {
obj = constructObj(val, obj, rowData[idx]);
});
return obj;
});
function constructObj(str, parentObj, data) {
if (str.split("/").length === 1) {
parentObj[str] = data;
return parentObj;
}
var curKey = str.split("/")[0];
if (!parentObj[curKey]) parentObj[curKey] = {};
parentObj[curKey] = constructObj(
str.split("/").slice(1).join("/"),
parentObj[curKey],
data
);
return parentObj;
}
console.log("resultresultresult", result);
};
But it returns like this:-
{
Name:"EGBL",
Time:"Today",
HeightOptions : [
{WidthRange:"12",
Options:"12.13.14.15.16"},
]
},{
Name:"",
Time:"",
HeightOptions : [
{WidthRange:"26",
Options:"12.13.14.15.16"
}]
}
So as you see code is reading through as rows and not combining in 1 object.
As far as possible I wish to have this done in vanilla JS without any other libraries.
Thanks folks.
Its the general way how the converter works. What you can do is, after getting the result in some JSON format, write another function that will convert to your final JSON format. Otherwise write your own CSV parser by reading it as raw text. Also you can play around by changing the csv format to different way so that you can get expected result (if possible).

Updating Json Value with that of another Json

I want to update automatically the value of comments_list with the values in the comments JSON object
const tweet = JSON.stringify({"tweet_id":1,"created_at":"2022-06-28","comments_list":[]})
const comments = JSON.stringify({"tweet_id":1,"commenter_id": 2"commenter_first_name":"tito","commenter_username":"tito_lulu"})
The final output should look like this
{"tweet_id":1,"created_at":"2022-06-28","comments_list":[{"commenter_id": 2"commenter_first_name":"tito","commenter_username":"tito_lulu"}]}
I'd work with those strings in an object form, otherwise string-manipulation could be slow in some cases.
This is by no means the fastest solution but perhaps the idea behind it can be helpful.
const tweet = [{
"tweet_id": 1,
"created_at": "2022-06-28",
"comments_list": []
}]; // There could be many tweet objects so wrap it in an array
const comments = [{
"tweet_id": 1,
"commenter_id": 2,
"commenter_first_name": "tito",
"commenter_username": "tito_lulu"
},
{
"tweet_id": 1,
"commenter_id": 5,
"commenter_first_name": "me-too",
"commenter_username": "me294"
}
]; // Same here, could be many comments right?
let UpdatedTweets = [];
// There are faster ways to do this, but for your question
tweet.forEach((tweet, tweetIndex) => {
// Loop each tweet
let post = tweet;
comments.forEach((comment, commentIndex) => {
if (comment.tweet_id == tweet.tweet_id) {
// we have a match lets combine them
tweet.comments_list.push({
commenter_id: comment.comment_id,
commenter_first_name: comment.commenter_first_name,
commenter_username: comment.commenter_username
});
}
});
UpdatedTweets.push(post);
});
console.log(JSON.stringify(UpdatedTweets));
The general idea is:
Parse the JSON into JS objects
Update the target object with the complementary information
Stringify the target object into JSON (only if you need to, eg. send the data to some other machine)
In your case:
const tweet = JSON.stringify({"tweet_id":1,"created_at":"2022-06-28","comments_list":[]});
const comments = JSON.stringify({"tweet_id":1,"commenter_id": 2,
"commenter_first_name":"tito","commenter_username":"tito_lulu"});
let o_tweet = JSON.parse(tweet)
, o_comments = JSON.parse(comments)
;
if (Array.isArray(comments)) { // Test whether that is a single or multiple comments
comments.forEach( c => { o_tweet.comments_list.push(c); });
} else {
o_tweet.comments_list.push(o_comments);
}
console.log(o_tweet);
// Only if needed:
// let newtweet = JSON.stringify(o_tweet)

loop through nested array and return item containing string from the first looped array

I have a nested array with data of file paths. What I was trying to do was get every item in the nested array and first check if the user inputted file name exists in the nested array and if it is found I wanted to log the whole path of that file name.
Currently, I have accomplished the first part but I can't figure out how to get the file path from the file name safely. Basically what I want is after the file name is verified by checking the nested array I want to log the file path which is the whole item in the nested array with that file name. How can I accomplish this?
One thing I have thought of is after verifying the file name run a loop of the nested array and compare every item by splitting it and checking if filename is found in that path. But is this method efficient for large arrays?
Plus note that the numbers in the path are dynamic so multiple items with the same file name but different identification numbers might occur.
c = [
[
['dd\\32323232323:this1', 'dd\\43564564:this2'],
['dd\\5464656646:this3', 'dd\\43543453:this2']
]
]
var currentFileNames = []
var mainDataArrayFilenames = c.forEach((entrys) => {
entrys.forEach((entry) => {
entry.map(function(entry) {
currentFileNames.push(entry.slice(entry.indexOf(':') + 1))
});
})
})
function test() {
const input = document.getElementById('input').value
if (currentFileNames.includes(input) == true) {
//expected full path of file name which is input
console.log("path")
} else {
console.log("name not found")
}
}
<input id="input">
<button onclick="test()">click</button>
Array.prototype.forEach won't return anything. You have to make use of Array.prototype.map if you want to store your result in a variable.
You can also flatten the Array using Array.prototype.flat.
c = [
[
['dd\\32323232323:this1', 'dd\\43564564:this2'],
['dd\\5464656646:this3', 'dd\\43543453:this2']
]
]
var mainDataArray = c
.flat(2) // flatten the array with depth of 3
.map(itm => {
try { // split each entry by :
return itm.split(':');
} catch(e) {
console.error(e);
return []
}
})
.filter(arr => arr.length); // filter all which have no valid information
// this is just for debug purpose and can be removed
mainDataArray.forEach(([path, filename]) => {
console.log(`path "${path}" / filename "${filename}"`)
});
Using the above part, you can search inside the parsed array using Array.prototype.filter and assign the result to a list.
function test() {
const input = document.getElementById('input').value,
list = mainDataArray.filter(([path, filename]) => filename == input);
if (list.length) {
// show all results
console.log(list.map(itm => itm.join(':')).join("\n"));
} else {
console.log("name not found");
}
}
c = [
[
['dd\\32323232323:this1', 'dd\\43564564:this2'],
['dd\\5464656646:this3', 'dd\\43543453:this2']
]
]
var mainDataArray = c
.flat(2) // flatten the array with depth of 3
.map(itm => {
try { // split each entry by :
return itm.split(':');
} catch(e) {
console.error(e);
return []
}
})
.filter(arr => arr.length); // filter all which have no valid information
// this is just for debug purpose and can be removed
mainDataArray.forEach(([path, filename]) => {
console.log(`path "${path}" / filename "${filename}"`)
});
function test() {
const input = document.getElementById('input').value,
list = mainDataArray.filter(([path, filename]) => filename == input);
if (list.length) {
// show all results
console.log(list.map(itm => itm.join(':')).join("\n"));
} else {
console.log("name not found");
}
}
<input type="text" id="input">
<input type="button" onclick="test()" value="Search">

assign value of a property of "clearsky" from "object" to weather so to pass through src in the image tag

The expected result is to first find weather string from array of object "objects" and then set value of "weather" variable equal to value of of clearsky value(i.e. link).
let weather = "clearsky";
const objects = [
{ "clearsky": "http://openweathermap.org/img/wn/11d#2x.png" },
{ "cloudy": "http://openweathermap.org/img/wn/19d#2x.png" },
{ "rainny": "http://openweathermap.org/img/wn/10d#2x.png" },
{ "sunny": "http://openweathermap.org/img/wn/12d#2x.png" },
];
//expected result is to first find weather string from array of object "objects" and then set value of weather equal to value of
weather = {
/* <img src={weather} alt=""/> */
};
I assume you would like to write some sort of function that displays your image corresponding to the right value (weather type) in your objects array.
I added these <img> tags (for displaying in your html).
<img id="img-clearsky">
<img id="img-rainy">
I changed the structure of your objects array a bit so that property values can be accessed in a simple way. I made a function that loops through your array and if it finds a match for your objects type, it uses the corresponding imgurl to load as src of the element with given id.
const objects = [{
"type": "clearsky",
"imgurl": "http://openweathermap.org/img/wn/11d#2x.png"
},
{
"type": "rainy",
"imgurl": "http://openweathermap.org/img/wn/10d#2x.png"
}
];
function displayImage(weatherType, elemid) {
const imgelem = document.getElementById(elemid); // Get img element by id.
// If element with given id exists.
if (imgelem !== null) {
// Loop through objects.
for (let i = 0; i < objects.length; i++) {
// Check if there is a match.
if (objects[i].type === weatherType) {
imgelem.src = objects[i].imgurl; // Fill up <img src> with corresponding url.
}
}
} else {
console.log(`No img with id: ${elemid} found.`);
}
}
// Call display with type and id.
displayImage('clearsky', 'img-clearsky');
displayImage('rainy', 'img-rainy');
There are multiple ways of doing this, but I would wrap this logic with a function - especially if you plan on splitting this into several constants.
const WeatherTypeImage = (weatherType = 'clearsky') =>
Object.assign(document.createElement('img'), {
src: {
"clearsky": "http://openweathermap.org/img/wn/11d#2x.png",
"cloudy": "http://openweathermap.org/img/wn/19d#2x.png",
"rainny": "http://openweathermap.org/img/wn/10d#2x.png",
"sunny": "http://openweathermap.org/img/wn/12d#2x.png",
}[weatherType],
alt: '',
})
document.querySelector('.js-container').appendChild(
WeatherTypeImage()
)
<div class="js-container"></div>
Or, if you would prefer to have an HTML string template:
const WeatherTypeImage = (weatherType = 'clearsky') => {
const src = {
"clearsky": "http://openweathermap.org/img/wn/11d#2x.png",
"cloudy": "http://openweathermap.org/img/wn/19d#2x.png",
"rainny": "http://openweathermap.org/img/wn/10d#2x.png",
"sunny": "http://openweathermap.org/img/wn/12d#2x.png",
}[weatherType]
return Object.assign(document.createElement('template'), {
innerHTML: `<img src="${src}" alt="">`
}).content
}
document.querySelector('.js-container').appendChild(
WeatherTypeImage()
)
<div class="js-container"></div>

How can I merge individual object values?

I have the following problem:
I want to read out my table names from a SQL database and then make a comparison as to whether it already exists. I know there is the formula IF EXISTS ... but IF doesn't work .. So here's my previous variant:
First i extracted everything before the filename.csv (C:\Users\Frederic\Desktop\Drag&Drop...) and then the ".csv". Do not be surprised why 51;) The filename is so long
var filename = filePath.slice(51);
var richtigername = filename.replace(".csv","").toString();
console.log(richtigername)
here the result in the console:
for example: fxbewertung
As a second step I let me show the file names:
connection.query('Show tables from datein', function(err, datein) {
let string = JSON.stringify(datein);
let json = JSON.parse(string);
console.log(json)
here the result in the console:
[ { Tables_in_datein: 'fxbewertung' },
{ Tables_in_datein: 'kontoauszug' },
{ Tables_in_datein: 'lsreport' } ]
Furthermore, I extracted the values (name of the SQL tables):
for (var k = 0; k < json.length; k++) {
var werte = Object.values(json[k])
console.log(werte)
};
here the result in the console:
[ 'fxbewertung' ]
[ 'kontoauszug' ]
[ 'lsreport' ]
Now I don't know how i can take the comparison that for example for the file fxbewertung exist a database ('fxbewertung').
My consideration is to somehow browse the individual arrays .. or merge and then browse. At the end should come out true or false
P.S .: it may well be complicated yet but I'm not a real programmer or something;)
Best regards
Frederic
You can use some() method to check if a table exists for that filename.
var tableExists = tables.some(item => item['Tables_in_datein'] === filename);
Live Example:
var tables = [{
Tables_in_datein: 'fxbewertung'
},
{
Tables_in_datein: 'kontoauszug'
},
{
Tables_in_datein: 'lsreport'
}
];
var filename = 'fxbewertung';
var tableExists = tables.some(item => item['Tables_in_datein'] === filename);
if (!tableExists) {
// Table not found for filename.
} else {
// Table found. Do something.
}
Assuming you finished executing your query and stored the data as following:
const queryResult = [ { Tables_in_datein: 'fxbewertung' },
{ Tables_in_datein: 'kontoauszug' },
{ Tables_in_datein: 'lsreport' } ]
You'll then need to map this array to extract the values and store them in a single array like so:
const values = queryResult.map(e=>e[Object.keys(e)[0]]) // ["fxbewertung", "kontoauszug", "lsreport"]
Since you're looking for a true/false result by giving a file name, you'll need to use indexOf to achieve that.
const valueExists = filename => values.indexOf(filename) !== -1
After that execute valueExists with the file name you're looking for:
valueExists("kontoauszug"); // true
valueExists("potato"); // false
Hope this helps!
An efficient solution could be to use Array.prototype.find(). Where it would return from the moment it finds a truthy value and would not iterate till the end (unless the match exists at the end).
Demo Code:
const tablesArr = [
{
Tables_in_datein: "fxbewertung"
},
{
Tables_in_datein: "kontoauszug"
},
{
Tables_in_datein: "lsreport"
}
];
const tabletoFind = "fxbewertung";
const tableFound = tablesArr.find(item => item["Tables_in_datein"] === tabletoFind) ? true: false;
console.log(tableFound);
if(tableFound){
//*yes table found*/
}else{
///*nope table not found*/
}

Categories

Resources