Console output for function with multiple optional arguments - javascript

Console output for function with multiple arguments
I have this class method:
searchForProduct({productName, manufacturer, seller}, itemsPerPage = 20, onlyAvailable = true) {
console.log(Searching for...) // Here's what is my question about
//do stuff
}
How can I print out all arguments that I'm passing to the method?
What a want to achieve is:
searchForProduct({productName: laptop});
// Output:
"Searching for productName: 'laptop'"
// or
searchForProduct({productName: "laptop", manufacturer: "Dell"});
// Output:
"Searching for productName: 'laptop', manufacturer: 'Dell'"
And so on...
Also (if it's possible with any approach that will be proposed) I don't want to print out default itemsPerPage and onlyAvailable even if it will be passed to the method.
UPD:
Wow, I was not expecting this amount of such neat approaches in the answers.
However I should admit that I'm not allowed to change this function. Basically I just need to add this output form my personal needs since changing this method will "break everything".
I apologize for time that you took to propose passing object instead of destructed arguments. I'll upvote your answers anyway
UPD2:
I tried a couple of suggestion but still didn't achieved the perfect result:
searchForProduct({productName, manufacturer, seller}, itemsPerPage = 20, onlyAvailable = true) {
function buildString({firstArgument, secondArgument, thirdArgument}) {
return {
productName: (firstArgument !== undefined) ? firstArgument : "",
manufacturer: (secondArgument !== undefined) ? secondArgument : "",
seller: (thirdArgument !== undefined) ? thirdArgument : ""
}
}
const searchString = buildString({productName, manufacturer, seller})
const displayStr = Object.entries(searchString)
.map(([key, val]) => key + ': ' + val)
.join(', ');
console.log('Searching for', displayStr);
//do stuff
}
The buildString function is super ugly but it works without changing the original function.
However I have faced an issue when not all arguments are passed
It will look like:
"Searching for productName: "laptop", manufacturer: "Dell", seller: undefined
I tried:
function buildString({firstArgument, secondArgument, thirdArgument}) {
return {
productName: (firstArgument !== undefined) ? firstArgument : delete productName,
manufacturer: (secondArgument !== undefined) ? secondArgument : delete manufacturer,
seller: (thirdArgument !== undefined) ? thirdArgument : delete seller
}
}
But this gives me:
"Searching for productName: "laptop", manufacturer: "Dell", seller: true
According to MDN:
When trying to delete a property that does not exist, true is
returned
However property does exist - it's value does not. So what I tried to above is to delete the object key if it does not have a value.
Currently no success

I wouldn't destructure the first argument - instead, stringify it or iterate over its entries to extract its keys and values:
const searchForProduct = (obj) => {
console.log('Searching for', JSON.stringify(obj));
};
searchForProduct({productName: 'laptop'});
searchForProduct({productName: "laptop", manufacturer: "Dell"});
const searchForProduct = (obj) => {
const displayStr = Object.entries(obj)
.map(([key, val]) => key + ': ' + val)
.join(', ');
console.log('Searching for', displayStr);
};
searchForProduct({productName: 'laptop'});
searchForProduct({productName: "laptop", manufacturer: "Dell"});
You can also use a wrapper function that does the same thing:
const searchForProductWrapper = (obj, itemsPerPage = 20, onlyAvailable = true) => {
const displayStr = Object.entries(obj)
.map(([key, val]) => key + ': ' + val)
.join(', ');
console.log('Searching for', displayStr);
searchForProduct(obj, itemsPerPage, onlyAvailable);
};
const searchForProduct = (obj, itemsPerPage, onlyAvailable) => {
console.log('true searchForProduct', obj, itemsPerPage, onlyAvailable);
};
searchForProductWrapper({productName: 'laptop'}, 10);
searchForProductWrapper({productName: "laptop", manufacturer: "Dell"});

How can I print out all arguments that I'm passing to the method?
Its more like you would like to see object properties of first argument
function searchForProduct(data, itemsPerPage = 20, onlyAvailable = true) {
console.log(`Searching for ${JSON.stringify(Object.entries(data))}`)
}
criteria = {
productName: '',
manufacturer: ''
}
searchForProduct(criteria)

Related

The best way to filter down a dataSet with javascript

The idea was to query a dataset with querystring params. I only want the "records" to match only what was queried.
Dataset
{
1111:
{
Category: "Education"
Role: "Analyst"
}
2222:
{
Category: "Communications and Media"
Role: "Analyst"
}
3333:
{
Category: "Public Sector"
Role: "Something else"
}
4444:
{
Category: "Public Sector"
Role: "Something else"
}
...
}
[[Prototype]]: Object
I'm sending in qString
Category: (2) ['Communications and Media', 'Education']
Role: ['Analyst']
length: 0
[[Prototype]]: Array(0)
I'd like to loop over that and filter/reduce so I only have records that match. Sort of an and instead of an or.
dataSet is an Object of objects. Thoughts? Thanks in advance.
export const Filtered = (qStrings, dataSet) => {
const filtered = [];
Object.entries(qStrings).forEach(([field]) => {
qStrings[field].forEach((value) => {
filtered.push(
..._.filter(dataSet, (sess) => {
if (sess[field] && sess[field].toString() === value.toString()) {
return sess;
}
})
);
});
});
return _.uniq(filtered);
};
geez, I figured it out with a colleague who's way smarter than me wink Jess!
export const Filtered = (qStrings, dataSet) => {
let filtered = [];
Object.entries(qStrings).forEach(([field], idx) => {
let source = filtered;
if (idx === 0) {
source = dataSet;
}
filtered = _.filter(source, (sess) => {
return sess[field] && sess[field].includes(qStrings[field]);
});
});
return _.uniq(filtered);
};
Now to clean this up.
Not sure if this solves your problem exactly, but you can apply this logic without mutation for a much cleaner function.
export const matches = (qStrings, dataSet) =>
Object.entries(dataSet).reduce((acc, [key, value]) =>
Object.entries(value).every(([rKey, rValue]) => qStrings[rKey]?.includes(rValue))
? { ...acc, [key]: value }
: acc,
{});
This will return records 1111 and 2222 because they match one of the categories and the role in qStrings.

How do I eliminate all these if-else

I have written this code to filter a "books" array, depending on the author or genre have been given as a parameter:-
//This code works perfectly fine as is
allBooks: (root, args) => {
if (args.author === undefined && args.genre === undefined) {
return books
} else if (args.author === undefined) {
return books.filter((book) => book.genres.includes(args.genre))
} else if (args.genre === undefined) {
return books.filter((book) => book.author === args.author)
} else {
const booksFilter = books.filter((book) => book.author === args.author)
return booksFilter.filter((book) => book.genres.includes(args.genre))
}
}
I believe there must be some way to write this more "professionally" without using all these if-else. So if anyone knows a better way, I'll appreciate it.
[Edited]
Thanks to all, I decided to go with ghostkraviz solution, code looks like this now:
allBooks: (root, args) => {
return books.filter((book) => {
const filteredGenres = book.genres.filter((genre) =>
genre.includes(args.genre || "")
);
return book.author.includes(args.author || "") && filteredGenres.length > 0;
})
}
You could take an array for filtering with key/value pairs, like
filters = [
['author', 'eliot'],
['genre', 'fiction']
]
and an object for storing special type of searching, like
methods = {
genre: 'includes'
}
Together, you get the following function
result = books.filter(book => filter.every(([key, value]) => key in methods
? book[key][methods[key]](value)
: book[key] === value
));
Because of Array#every's return value of true for empty arrays, you need no further action to get all books.
since String.prototype.includes actually checks if a string maybe found within another string (the parameter) MDN String.prototype.includes. that means, for undefined args you could default it to empty string. Empty strings will return true if checked with .includes from any string.
you only check for 2 args which are the author & genre.
here's the example:
const books = [
{author: "A", genres: ["Horror", "romance"]},
{author: "B", genres: ["romance"]},
{author: "X", genres: ["science"]},
{author: "C", genres: ["science", "Horror"]}
];
const allBooks = (root, args) => {
return books.filter(book => {
const filteredGenres = book.genres.filter(genre =>
genre.includes(args.genre || "")
);
return book.author.includes(args.author || "") && filteredGenres.length > 0;
});
};
console.log('filtered Horror = ', allBooks({}, {genre: 'Horror'}));
console.log('filtered A and Horror = ', allBooks({}, {author: 'A', genre: 'Horror'}));
console.log('filtered romance = ', allBooks({}, {genre: 'romance'}));
// for all books result
console.log('filtered romance = ', allBooks({}, {}));
// for an author result
console.log('filtered author(A) = ', allBooks({}, {author:"A"}));
I don't know if the version below is written more "professionally" as it looks messy, but it is a single line, and does not use if-else.
books = [{
author: "a",
genres: ["a"]},
{author: "a",
genres: ["a", "b"]},
{author: "b",
genres: ["c", "b"]
}];
allBooks = (root, args) => {
return (!args.author && !args.genre) ? books : (!args.author) ?
books.filter((book) => book.genres.includes(args.genre)):(!args.genre)?
books.filter((book) => book.author === args.author) :
(books.filter((book) => book.author === args.author)).filter((book) =>
book.genres.includes(args.genre));
}
console.log(allBooks("",{genre: "b"}));
//console:
//0: {author: "a", genres: Array(2)}
//1: {author: "b", genres: Array(2)}
I like to use if-else, as it's more readable. We can also eliminate the else-ifs since each of the if statement has a return.
This is just a simplified version of your existing codes without the else-ifs and curly brackets
allBooks: (root, args) => {
const { author, genre } = args;
if (!author && !genre) return books;
if (!author) return books.filter((book) => book.genres.includes(genre))
if (!genre) return books.filter((book) => book.author === author);
return books.filter((book) => book.author === author).filter((book) => book.genres.includes(genre))
}
Sometimes it's not about writing codes professionally.
It's about writing codes that you can easily understand.

Function is getting called multiple times

I have an array of objects
const data = [{
Description: "confirm"
Id: "1"
Name: "confirm"
Value: "VIP:confirm"
}, {
Description: "validate"
Id: "2"
Name: "validate"
Value: "VIP:validate"
}, {
Description: "Sent"
Id: "2"
Name: "Sent"
Value: "VIP:Sent"
}]
Now, I am trying to get the description by passing the value:
const valuesObject = [
"VIP:Confirmed",
"VIP:Validated",
"VIP:Sent"
]
Now, Values data is like
const getDescription = (
value: string,
Values: Array < >
) => {
let allValues = _.find(Values, item => item.Value === value)
return resolve(allValues)
}
const resolve = (object) => {
return object?.Description ? object.Description : object?.Name ?? ''
}
Now, here I am doing ,
const status = valuesObject.map((value) => {
return getDescription(value, data)
})
return status.join('/')
I was expecting it should return me Confirmed/Validated/Sent
It returns but function gets called multiple times. can any one help me with this ?
Use _.intersectionWith() to get objects with the Value property that matches one of an array of values. Then map to get the Description or Name:
const getDescription = (arr, values) => _.map(
_.intersectionWith(arr, values, (o, v) => o.Value === v), // get all objects with matching values
({ Description, Name = '' }) => Description || Name // map to description / name / empty string
).join('/')
const data = [{"Description":"Confirmed","Id":"1","Name":"confirm","Value":"VIP:Confirmed"},{"Description":"Validated","Id":"2","Name":"validate","Value":"VIP:Validated"},{"Description":"Sent","Id":"2","Name":"Sent","Value":"VIP:Sent"}]
const valuesObject = ["VIP:Confirmed","VIP:Validated","VIP:Sent"]
const result = getDescription(data, valuesObject)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.19/lodash.js"></script>
Using lodash/fp you can generate the getDescription() function with _.flow():
const getDescription = _.flow(
_.intersectionWith((o, v) => o.Value === v), // get all objects with matching values
_.map(({ Description, Name = '' }) => Description || Name), // map to description / name / empty string
_.join('/')
)
const data = [{"Description":"Confirmed","Id":"1","Name":"confirm","Value":"VIP:Confirmed"},{"Description":"Validated","Id":"2","Name":"validate","Value":"VIP:Validated"},{"Description":"Sent","Id":"2","Name":"Sent","Value":"VIP:Sent"}]
const valuesObject = ["VIP:Confirmed","VIP:Validated","VIP:Sent"]
const result = getDescription(data, valuesObject)
console.log(result)
<script src='https://cdn.jsdelivr.net/g/lodash#4(lodash.min.js+lodash.fp.min.js)'></script>

Iterate object keys to replace the selected one with desired keys using es6 array functions

I have a requirement to replace the available keys with the desired keys in an object for which I was trying to execute below code, which later I found out to be incorrect usage of filter for desired output. hence I need help in getting the desired results using es6 array functions.
const columns = Object.keys(someArray).filter((columnName) => {
if (someCheck === "somecheck") {
if (columnName === 'MyName') {
const newcolumnName = `Pranav`;
return newcolumnName;
} else if (columnName === 'YourName') {
const newcolumnName = `Alex`;
return newcolumnName;
}
} else {
return (columnName !== 'sometingelse') ? columnName : '';
}
}
);
Here the someArray is as below:
someArray{
abc:"djfhdjf",
xyz:"ssss",
MyName:"onename",
YourName:"somename",
sometingelse:'somevalue'
}
I am expecting columns to be:
columns{
abc:"djfhdjf",
xyz:"ssss",
Pranav:"onename",
Alex:"somename",
sometingelse:'somevalue'
}
Please suggest how can I achieve the above expected output?
Note: I dont want to use function keyword in callbacks to avoid eslint errors
You could filter the wanted keys for replacement and replace the keys by using a new key and eleting the old one.
const
object = { abc: "djfhdjf", xyz: "ssss", MyName: "onename", YourName: "somename", sometingelse: 'somevalue' },
replacements = { MyName: 'Pranav', YourName: 'Alex', sometingelse: '' };
Object
.keys(object)
.filter(k => k in replacements)
.forEach(k => {
object[replacements[k]] = object[k];
delete object[k];
});
console.log(object);
For generating an object, you could map new objects and assign them to a single object.
const
object = { abc: "djfhdjf", xyz: "ssss", MyName: "onename", YourName: "somename", sometingelse: 'somevalue' },
replacements = { MyName: 'Pranav', YourName: 'Alex', sometingelse: '' },
result = Object.assign(...Object
.entries(object)
.map(([k, v]) => ({ [k in replacements ? replacements[k] : k]: v }))
);
console.log(result);
const obj = {
abc: 'djfhdjf',
xyz: 'ssss',
MyName: 'onename',
YourName: 'somename',
sometingelse: 'somevalue'
};
const newObj = Object.keys(obj).reduce((acc, key) => {
if (key === 'MyName') {
acc.newMyName = obj[key];
} else if (key === 'YourName') {
acc.newYourName = obj[key];
} else {
acc[key] = obj[key];
}
return acc;
}, {});
console.log('newObj = ', newObj);
Here is my approach, a bit long solution, but its on purpose so you can see how to do it simple without too much abstraction:
const someArray = {
abc:"djfhdjf",
xyz:"ssss",
MyName:"onename",
YourName:"somename",
sometingelse:'somevalue'
}
let foo = Object.keys(someArray).map(key => {
if(key === 'MyName') {
return 'Alex'
} else if(key === 'YourName') {
key = 'Pranav'
}
return key;
})
let bar = Object.entries(someArray).map((el, i) => {
el[0] = res[i];
return el;
})
let baz = r.reduce((acc, el)=>{
acc[`${el[0]}`] = el[1];
return acc;
},{})
console.log(baz);
You could use .reduce like so. It uses a similar idea that Nina proposed by using an object to hold your replacements. Here I have used the spread syntax to add the changed key to the accumulated object, along with it's associated value.
const someArray = {abc: "djfhdjf", xyz: "ssss", MyName: "onename", YourName: "somename", sometingelse: 'somevalue'},
toUse = {MyName: "Pranav", YourName: "Alex"}, // define the keys you want to change and what they should change to
res = Object.keys(someArray).reduce((acc, key) =>
({...acc, [key in toUse ? toUse[key] : key]:someArray[key]})
, {});
console.log(res);
I am running a reduce on the keys of some array starting with an empty object. The ...acc spreads out all the properties in the reduced object. ...{ [keysMap[key] || key]: obj[key] } checks if the current key is present in keysMap.If it is present,it uses that key (keysMap[key]) otherwise it just uses the keys of the existing object.(|| key).Hope that makes sense
const renameKeys = (keysMap, obj) =>
Object.keys(obj).reduce(
(acc, key) => ({
...acc,
...{ [keysMap[key] || key]: obj[key] }
}),
{}
)
const columns = renameKeys({'MyName':'Pranav','YourName':'Alex'},someArray)

How do I extract a property value from an object called with .find()? [duplicate]

This question already has an answer here:
Why doesn't my arrow function return a value?
(1 answer)
Closed 5 years ago.
I have this code in my function
let packName: string = respPack.find(a => {a.id == 'name_input'}).answer.replace(/ /,'_');
What I'm trying to do is find the object by matching the id and extract the value of it's answer property. As of right now I'm getting an error telling me
cannot read property answer of undefined.
Am I trying to do this the right way? Here's the rest of my function so you can see what's going on.
saveResponses(){
const respPack = this.ResponseList;
const sendTarget: FirebaseObjectObservable<any> = this.afdb.object('/submissions');
let dataLoad:{ [prop : string]: Array<any> } = {};
let packName: string = respPack.find(a => {a.id == 'name_input'}).answer.replace(/ /,'_');
respPack.forEach( a => {
if(a.answer){
let data = { question: a.question, answer: a.answer, id: a.id };
dataLoad[packName].push(data);
}
else if(a.responses){
let dataChunk = { question: a.question, id: a.id, responses: Array<any> };
a.responses.forEach(resp => {
let respChunk = { response: resp.response, value: resp.value, id: resp.id };
dataChunk.responses.push(respChunk);
});
dataLoad[packName].push(dataChunk);
}
});
sendTarget.set(dataLoad);
}
If you're using an arrow function with {} you have to include return.
Eg:
a => {return a.id == 'name_input'}
Also, find will return undefined if no items are found, you'll have to deal with those cases.
Here is a complete example:
let packName: string = "";
let foundElement = respPack.find(a => {
return a.id == 'name_input';
});
if(foundElement){
packName = foundElement.answer.replace(/ /,'_');
}
Or if you want to keep it one line.
let packName: string = ((respPack.find(a => a.id == 'name_input') || {}).answer || "").replace(/ /,'_');
yourArray.find(element) will retrun the element following is an example
find more about find()
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findhttps://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find
var inventory = [
{name: 'apples', quantity: 2},
{name: 'bananas', quantity: 0},
{name: 'cherries', quantity: 5}
];
function findCherries(fruit) {
return fruit.name === 'cherries';
}
console.log(inventory.find(findCherries));
// { name: 'cherries', quantity: 5 }
You have the issue in your find method calling
a => {return a.id == 'name_input'}
find more about arrow function
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions

Categories