Looping through array and print data based on quantity in nodejs - javascript

Hi all i have a small requirement where I will have the below array,
{items:[{upc:a,quantity:2},{upc:b,quantity:3}]}
I need to convert this to the below format
{products:[{barcode:a},{barcode:a},{barcode:b},{barcode:b},{barcode:b}]}
any ideas regarding this will be helpful

Yes, javascript have native methods that allow to do it.
const source = {items:[{upc:'a',quantity:2},{upc:'b',quantity:3}]}
const res = source.items.map(item => new Array(item.quantity).fill({barcode: item.upc})).flat()
console.log({products: res});
PS: The output looks a bit weird, because several fields contains reference to same object - the console.log is telling it to you so it does not have to repeat output for each object.
But the object itself contains the values you need. If you need to have separate objects (i.e. if you want to update just one of them), then you can do it as following:
const source = {items:[{upc:'a',quantity:2},{upc:'b',quantity:3}]}
const res = source.items.map(item => new Array(item.quantity).fill(0).map(x => ({barcode: item.upc}))).flat()
console.log({products: res});

Related

How to dynamically get column names from TypeORM?

I have tried
let modelClass = (await import('#models/' + modelName + '.ts'))[modelName];
let keys = Object.keys(modelClass);//no column names included, only custom helper vars i.e. NoteColumnVarcharLength
let keys = Object.keys(new modelClass());//nothing at all in this model
Is there a helper function in the library to automatically get them all?
As proposed in the issue on repository of that library (link) can be solved by using
connection.getMetadata("User").columns
The accepted answer looks incomplete, and many questions appeared in my head when I tried to apply the answer. So I write my own to cover the question well.
To get column names, we must first retrieve EntityMetadata. We can retrieve EntityMetadata from Connection, which we can get in any comfortable way.
import { getConnection, getManager } from 'typeorm';
const connection1 = getConnection();
const connection2 = getManager().connection;
Next, we need to retrieve entity metadata.
We can do it via
const metadata = connection.getMetadata(modelClass);
Inside entity medatata you can use several options to find a column. If you need to look for all columns, you need to use columns. If you are going to look only over user-defined columns, you can use ownColumns.
These properties contains arrays with ColumnMetadata.
If you just need a list of user-defined columns, you can just return a mapped values of propertyName.
const columns = metadata.columns.map((column) => column.propertyName);
In my case, I needed to find database column name by a property name.
I used a find() for my case.
const column = metadata.ownColumns.find((column) => column.propertyName === propertyName);
If I need to find several column I would create a map between propertyName and databaseName;
const columnMap = Object.fromEntries(
metadata.ownColumns((column) => ([column.propertyName, column.databaseName]))
);

Prevent pushing to array if duplicate values are present

I'm mapping an array and based on data i'm pushing Option elements into an array as follows
let make_children: any | null | undefined = [];
buyerActivityResult && buyerActivityResult.simulcastMyAccount.data.map((item: { make: {} | null | undefined; }, key: any) => {
make_children.push(
<Option key={key}>{item.make}</Option>
);
});
Following data array has several objects and these objects have an attribute called model.
buyerActivityResult.simulcastMyAccount.data
I want to prevent pusing Options to my array if the attribute model has duplicate data. It only has to push once for all similar model values.
How can i do it?
I tried something like this
buyerActivityResult && buyerActivityResult.simulcastMyAccount.data.map((item: { model: {} | null | undefined; }, key: any) => {
model_children.indexOf(item.model) === -1 && model_children.push(
<Option key={key}>{item.model}</Option>
);
});
But still duplicate values are being pushed into my array.
Its difficult to tell what you are trying to achieve but it looks like a map may not be the right tool for the job.
A map returns the same sized length array as that of the original array that you are calling map on.
If my assumptions are correct, your buyerActivityResult.simulcastMyAccount.data array has duplicate values, and you want to remove these duplicates based on the model property? One way to achieve this would be to use the lodash library for this, using the uniq function:
const uniqueResults = _.uniq(buyerActivityResult.simulcastMyAccount.data, (item) => item.model);
The Array.prototype.map() method is supposed to be used for manipulating the data contained into the array performing the operation. To manipulate data from other variables I recommend to use a for-loop block.
If item.model is an object, the function Array.prototype.indexOf() always returns -1 because it compares the memory address of the objects and does not do a deep comparison of all properties values.
The usual solution to remove duplicate data from an array is converting the Array into a Set then back to an Array. Unfortunately, this works only on primary type values (string, number, boolean, etc...) and not on objects.
Starting here, I will review your source code and do some changes and explain why I would apply those changes. First of all, assuming the make_children array does not receive new attribution later in your code, I would turn it into a constant. Because of the initialization, I think the declaration is overtyped.
const make_children: any[] = [];
Then I think you try to do too much things at the same time. It makes reading of the source code difficult for your colleagues, for you too (maybe not today but what about in few weeks...) and it make testing, debugging and improvements nearly impossible. Let's break it down in at least 2 steps. First one is transforming the data. For example remove duplicate. And the second one create the Option element base on the result of the previous operation.
const data: { make: any }[] = buyerActivityResult?.simulcastMyAccount?.data || [];
let options = data.map((item) => !!item.model); // removing items without model.
// Here the hard part, removing duplicates.
// - if the models inside your items have a property with unique value (like an ID) you can implement a function to do so yourself. Take a look at: https://stackoverflow.com/questions/2218999/remove-duplicates-from-an-array-of-objects-in-javascript
// - or you can use Lodash library like suggested Rezaa91 in its answer
options = _.uniq(data, (item) => item.model);
Now you only have to create the Option elements.
for (var i = 0; i < options.length; i++) {
model_children.push(<Option key={i}>{options[i].model}</Option>);
}
// OR using the Array.prototype.map method (in this case, do not declare `model_children` at the beginning)
const model_children:[] = options.map((opt:any, i:number) => <Option key={i}>{opt.model}</Option>);
Despite the lack of context of the execution of the code you provided I hope my answer will help you to find a solution and encourage you to write clearer source code (for the sake of your colleagues and your future self).
PS: I do not know anything about ReactJs. forgive me my syntax mistakes.

what are the ways to convert a simple array to 'nested array in javascript?

I want to capture screenshots on my remote virtual machine without using display. I came across a library in nodejs 'capture screenshot' (https://github.com/sindresorhus/capture-website) which is pretty straightforward.
Taking screenshot of a single link is fairly simple, however taking multiple screenshots becomes tricky. I don't want to put links manually as it will be time consuming. I am running a script to get all anchor tags in python and saving a csv file of links gathered. This is how it looks when I convert the saved csv file to a JavaScript array.
[ 'https://www.google.com/imghp?hl=en&tab=wi',
'https://maps.google.com/maps?hl=en&tab=wl',
'https://play.google.com/?hl=en&tab=w8',
'https://www.youtube.com/?gl=US&tab=w1',
'https://news.google.com/nwshp?hl=en&tab=wn',
'https://mail.google.com/mail/?tab=wm',
'https://drive.google.com/?tab=wo',
'https://www.google.com/intl/en/about/products?tab=wh',
'https://accounts.google.com/ServiceLogin?hl=en&passive=true&continue=https://www.google.com/',
'https://www.google.com/url?q=https://lifeinaday.youtube/%3Futm_source%3Dgoogle%26utm_medium%3Dhppcta%26utm_campaign%3D2020&source=hpp&id=19019062&ct=3&usg=AFQjCNEJMAD58Mjdnro8Mjm-RtJ3nfEIZA&sa=X&ved=0ahUKEwi98PWM4-HqAhVh1uAKHeYGCPwQ8IcBCAU'
]
I want to convert the above array to the array shown below.
[
['https://www.google.com/imghp?hl=en&tab=wi', 'anyanme'],
['https://maps.google.com/maps?hl=en&tab=wl','anyanme'],
['https://play.google.com/?hl=en&tab=w8','anyanme'],
['https://www.youtube.com/?gl=US&tab=w1','anyanme'],
['https://news.google.com/nwshp?hl=en&tab=wn','anyanme'],
['https://mail.google.com/mail/?tab=wm','anyanme'],
['https://drive.google.com/?tab=wo','anyanme'],
['https://www.google.com/intl/en/about/products?tab=wh','anyanme'],
['https://accounts.google.com/ServiceLogin?hl=en&passive=true&continue=https://www.google.com/','anyanme'],
['https://www.google.com/url?q=https://lifeinaday.youtube/%3Futm_source%3Dgoogle%26utm_medium%3Dhppcta%26utm_campaign%3D2020&source=hpp&id=19019062&ct=3&usg=AFQjCNEJMAD58Mjdnro8Mjm-RtJ3nfEIZA&sa=X&ved=0ahUKEwi98PWM4-HqAhVh1uAKHeYGCPwQ8IcBCAU','anyanme']
];
``
I am newbie to javscript and having a hard time to solve this. Any help will be appreciated.
It looks like the library you're using wants an array of pairs, with a URL and a file name.
I would map your original array to the array of pairs, but you'll have to figure out what you want to do for file names.
Assuming you have a function, named toFileName that will take a URL string and return a file name that you want for that URL, you could map the original array into the array of pairs like this:
const pairArr = originalArray.map((url) => {
const fileName = toFileName(url);
return [url, fileName];
});
If you just want to try to use the URL as the filename, you could just do this:
const pairArr = originalArray.map((url) => {
return [url, url];
});
This will be an issue with most URLs, though, as they'll have characters that are invalid for filenames. If you need a file extension with this approach, you could use string concatenation (url + '.png') for the second item in the returned pair.
You ca achieve it with a Map and regex
const arr = ['https://www.google.com/imghp?hl=en&tab=wi',
'https://maps.google.com/maps?hl=en&tab=wl',
'https://play.google.com/?hl=en&tab=w8',
'https://www.youtube.com/?gl=US&tab=w1',
'https://news.google.com/nwshp?hl=en&tab=wn',
'https://mail.google.com/mail/?tab=wm',
'https://drive.google.com/?tab=wo',
'https://www.google.com/intl/en/about/products?tab=wh',
'https://accounts.google.com/ServiceLogin?hl=en&passive=true&continue=https://www.google.com/',
'https://www.google.com/url?q=https://lifeinaday.youtube/%3Futm_source%3Dgoogle%26utm_medium%3Dhppcta%26utm_campaign%3D2020&source=hpp&id=19019062&ct=3&usg=AFQjCNEJMAD58Mjdnro8Mjm-RtJ3nfEIZA&sa=X&ved=0ahUKEwi98PWM4-HqAhVh1uAKHeYGCPwQ8IcBCAU',
'',
];
function extractName(str) {
const regex = /https:\/\/(.*?)\//g;
const ret = regex.exec(str);
return (ret && ret[1]) || null;
}
const items = arr.map(x => [
x,
extractName(x),
]);
console.log(items);

Looking up value from CSV and return result

I'm attempting to use Javascript to lookup a value in a CSV file and return it's corresponding value (basically what a VLOOKUP does in Excel)
I've been able to follow some examples separately of pulling the CSV data into an array, and I've seen some examples of looking up data in arrays - but for the life of me I can't figure out how to get both working.
For example, the CSV file stations.csv has the following data:
mac,name
69167f276e9g,LINE1
69167f276e9f,LINE2
What I want to be able to do is lookup the 'mac' value from the CSV, and return the corresponding 'name' value.
So if I look for '69167f276e9f' I want to get the value LINE2 back.
[EDIT: Adding the code I've tried using MauriceNino's suggestion - but getting error Uncaught TypeError: Cannot read property '1' of undefined at the line 'return result[1];' ]:
$.ajax('stations.csv').done(function(data) {
const lookup = (arr, mac) => {
let twoDimArr = dataArr.map(el => el.split(',')); // map it to array of arrays of strings (data)
let result = twoDimArr.filter(el => el[0] == mac)[0]; // Get the first element where the mac matches the first element in the array
return result[1]; // Return the second element in the array
};
let dataArr = data.split('\n');
dataArr .shift(); // Remove the first array element (The header)
let resultMac = lookup(dataArr, "69167f276e9f");
console.log(resultMac);
})
Here is a possible solution:
It first creates a two-dimensional array from your data
Then it searches the first element that has the specified mac as the first value
Then it returns the second element of that array
const data = `mac,name
69167f276e9g,LINE1
69167f276e9f,LINE2`;
const lookup = (arr, mac) => {
let twoDimArr = dataArr.map(el => el.split(',')); // map it to array of arrays of strings (data)
let result = twoDimArr.filter(el => el[0] == mac)[0]; // Get the first element where the mac matches the first element in the array
return result[1]; // Return the second element in the array
}
let dataArr = data.split('\n');
dataArr .shift(); // Remove the first array element (The header)
let resultMac = lookup(dataArr, '69167f276e9f');
console.log(resultMac);
I just finished writing a script doing almost the exact same thing.
What I did there is I streamed the csv file using the readline and fs libraries:
csvStream = readline.createInterface({
input: fs.createReadStream(filePath);
});
Then, I manually parsed each line of the csv using the line event of the readline stream library:
decompressedStream.on('line', (line) =>
My csv has kind of a special structure, so I had to do manual parsing and some modifications to values, but basically the idea is splitting the string you get as 'line' using line.split and your delimeter, and then push each value to a cell in the array.
what you get is an array that each cell in it represents one value in the csv line, and then you can push it to another big array which represents the whole csv and each cell in it represents a line.
after that, you can use the close event to know when the stream ended:
decompressedStream.on('close', () =>
and then its just a matter of using array.find the whole csv array to find the value you looking for:
let res = csvArray.find(val => val[0] == curMac)
and then
return res[1]
EDIT: i just saw MauriceNino's answer, which is very similar to mine.
notice the differences:
in his answer he splits only using the delimeter, wheres in my case you have option to add more calculation or modifications to data before pushing it to the array for each line, and also account for special cases like when you have the delimeter inside a value surrounded by "". thats why I needed to use the stream method.
His method is perfect for small files, but loading extremely large files directly to memory can be bad. I was working on 15GB csv files, so I had to use the stream. This way I could load a few line, process them and dump my data, and then again load a bulk of lines.
As it seems, his logic is better suited for your case, but I do not know the whole flow or purpose of your program, so I will leave my answer here for you to decide which is better suited for you :)
First convert the CSV to an Array of objects.
Than use Array.prototype.find() to find in that Array the Object which property value matches a desired string.
const csv2array = (csv, dlm = ',') => {
const [heads, ...lines] = csv.split('\n'), th = heads.split(dlm);
return lines.map(l => l.split(dlm).reduce((o, c, i) => (o[th[i]] = c.trim(), o), {}));
}
const findInCSV = (csv, p, v) => csv2array(csv).find(k => k[p] === v);
// Getting stuff:
const myCSV = `mac,name
69167f276e9g,LINE1
69167f276e9f,LINE2`;
console.log(findInCSV(myCSV, 'mac', '69167f276e9g')) // Get entire object by knowing the mac
console.log(findInCSV(myCSV, 'mac', '69167f276e9g').name) // Get name knowing the mac
console.log(findInCSV(myCSV, 'name', 'LINE1')) // Get entire object by knowing the name
console.log(findInCSV(myCSV, 'name', 'LINE1').mac) // Get mac knowing the name
Sounds like you need a dictionary, for example:
var myMappings = {
'69167f276e9f': 'LINE1',
'69167f276e9g': 'LINE2'
};
var myVal = myMappings['69167f276e9g'];
alert(myVal); // prints LINE2

Deduplicating using nodeJS

My goal is to take in a CSV file which contains approximately 4 million records and process each record while scrubbing the data of a particular field. The scrubbing process we have actually creates a reversible hash but is a time consuming process (almost 1 second). What I would like to do since there are only about 50,000 unique values for that field is to set them as properties of an object. Here is a pseudo example of how the object will be built. You can see that for duplicates I plan to just overwrite the existing value (this is to avoid having to loop through some if based search statement.
var csv = require('csv');
var http = require('http');
var CBNObj = new Object;
csv()
.fromPath(__dirname+'/report.csv',{
columns: true
})
.transform(function(data){
CBNObj[data['Field Value']] = data['Field Value'];
});
console.log(CBNObj);
This should create my object something like this.
myObj['fieldValue1'] = 'fieldValue1'
myObj['fieldValue2'] = 'fieldValue2'
myObj['fieldValue3'] = 'fieldValue3'
myObj['fieldValue1'] = 'fieldValue1'
myObj['fieldValue1'] = 'fieldValue1'
I have looked over some good posts on here about iterating over every property in an object (like this one Iterating over every property of an object in javascript using Prototype?) but I am still not exactly sure how to acccomplish what I am doing. How can I then take my object with 50k properties and essentially dump the values into an array so that I can end up with something like this?
myArray = ['fieldVaue1','fieldVaue2','fieldVaue3']
EDIT: I could also use some assistance on the first part here because I am getting a null value or undefined when I try and set the object properties. I also still need help then traversing through the object properties to build my array. Any help would be greatly appreciated.
You know that the keys of your object are the unique values you want. You just need an array. In node.js you can use Object.keys().
https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/keys
It's a standard way to take all the keys of an object (that aren't provided by the prototype chain) and put them into an array. So your example looks like this.
var csv = require('csv');
var AcctObj = new Object();
var uniqueArray;
csv()
.fromPath(__dirname+'/report.csv',{
columns: true
})
.on('data',function(data){
AcctObj[data['Some Field Value']] = data['Some Field Value'];
})
.on('end', function(){
uniqueArray = Object.keys(AcctObj);
});
Object.keys also does the hasOwnProperty check internally, so it's similar to the answer by #DvideBy0. It's just one step to the array you want.
var csv = require('csv');
var AcctObj = new Object();
csv()
.fromPath(__dirname+'/report.csv',{
columns: true
})
.on('data',function(data){
AcctObj[data['Some Field Value']] = data['Some Field Value'];
})
.on('end', function(){
for(var prop in AcctObj) {
if(AcctObj.hasOwnProperty(prop))
//Do something here....
}
});

Categories

Resources