javascript: Shortest code to find object key matching a pattern - javascript

Consider the object:
var myObj = {
hugeKey1: 'xxx',
hugeKey2: 'xxx',
hugeKey3: 'xxx',
hugeKey4: 'xxx',
prettyKey1: 'Only one'
};
Following is the code for getting a list of all keys with pattern hugeKey:
var filteredKeySet = _.filter(
Object.keys(data),
function (key) {
if (key.match(/hugeKey/i)) {
return true;
}
}
);
There is only one key named PrettyKey1, but this the number at the end is dynamic - it could be PrettyKey2 as well.
What's the shortest piece of code to find the first key with pattern match?
Something that looks like Object.keys(myObj).findFirstMatch(/PrettyKey/i);

In addition to previous answers, in case you need to perform such operation frequently and the target object is also changing you could write following utility function:
function matchBy(pattern) {
return obj => Object.keys(obj).find(k => k.match(pattern));
}
or
function findBy(pattern) {
return obj => Object.keys(obj).find(k => k.includes(pattern));
}
And then use them as :
var match = matchBy(/prettyKey/i);
var find = findBy("prettyKey");
....
console.log(match(myObj));
console.log(find(myObj));

From
function callback(elm){
if(elm.match(/prettyKey/i)) return true;
}
Object.keys(myObj).findIndex(callback);
to
Object.keys(myObj).findIndex(key=>key.match(/PrettyKey/i))
or
Object.keys(myObj).findIndex(key=>key.includes('prettyKey'))

According to your requirements, this is probably what you need:
const firstMatchedKeyNameInObject = Object.keys(myObj).find(keyName => keyName.includes('prettyKey'));

Ok so found a possible answer almost immediately after posting:
Object.keys(myObj).findIndex(x=>x.match(/PrettyKey/i))
Just needed to use the => based index search on the keys.
Wonder if there is a faster way of doing this via lodash.

Related

How to use If statement in map function?

I'm using the map function to assign and populate table names only if object['Size/ scale of support']. is equal to a data that I'm passing using props. I've seemed to have got the logic right but the map is not allowing me to use my if statement. Is there a way I can use an if statement on my map function?
updateData(result) {
const data = result.data;
console.log(data);
let new_data = []
data.map(
(object) => {
if (object['Size/ scale of support'].toLowerCase() === this.props.data.toLowerCase()) {
console.log("support",object['Size/ scale of support'])
new_data.push(
{
scale_of_support : object['Size/ scale of support'],
size_of_funding_instrument : object['Size of funding instrument'],
maturity_of_innovation_candidate : object['Maturity of innovation/ candidate'],
maturity_of_innovation_innovator : object['Maturity of innovation/ innovator'],
web_and_profile : object['Web and profile'],
source_of_information : object['Source of information'],
funding_instrument_name : object['Funding instrument name'],
}
)
}
}
)
this.setState({ cvs: new_data });
}
This code works:
var foo = [1,2,3];
foo.map(item => {
if (item === 2) console.log(item * 2);
return item;
});
which proves that your code is correct. If you use Typescript/Lint, then you may encounter errors because of this. If that's the case, you have several options to solve it:
Refactor
You can refactor your code, write a function that performs the conditional and its operation and only call that function from inside map.
Ternary operator
You may use the ternary operator as well, like:
mycondition ? myFunction() : myOtherValue;

Javascript's method forEach() creates array with undefined keys

I am building a simple todo app, and I'm trying to get the assigned users for each task. But let's say that in my database, for some reason, the tasks id starts at 80, instead of starting at 1, and I have 5 tasks in total.
I wrote the following code to get the relationship between user and task, so I would expect that at the end it should return an array containing 5 keys, each key containing an array with the assigned users id to the specific task.
Problem is that I get an array with 85 keys in total, and the first 80 keys are undefined.
I've tried using .map() instead of .forEach() but I get the same result.
let assignedUsers = new Array();
this.taskLists.forEach(taskList => {
taskList.tasks.forEach(task => {
let taskId = task.id;
assignedUsers[taskId] = [];
task.users.forEach(user => {
if(taskId == user.pivot.task_id) {
assignedUsers[taskId].push(user.pivot.user_id);
}
});
});
});
return assignedUsers;
I assume the issue is at this line, but I don't understand why...
assignedUsers[taskId] = [];
I managed to filter and remove the empty keys from the array using the line below:
assignedUsers = assignedUsers.filter(e => e);
Still, I want to understand why this is happening and if there's any way I could avoid it from happening.
Looking forward to your comments!
If your taskId is not a Number or autoconvertable to a Number, you have to use a Object. assignedUsers = {};
This should work as you want it to. It also uses more of JS features for the sake of readability.
return this.taskLists.reduce((acc, taskList) => {
taskList.tasks.forEach(task => {
const taskId = task.id;
acc[taskId] = task.users.filter(user => taskId == user.pivot.task_id);
});
return acc;
}, []);
But you would probably want to use an object as the array would have "holes" between 0 and all unused indexes.
Your keys are task.id, so if there are undefined keys they must be from an undefined task id. Just skip if task id is falsey. If you expect the task id to possibly be 0, you can make a more specific check for typeof taskId === undefined
this.taskLists.forEach(taskList => {
taskList.tasks.forEach(task => {
let taskId = task.id;
// Skip this task if it doesn't have a defined id
if(!taskId) return;
assignedUsers[taskId] = [];
task.users.forEach(user => {
if(taskId == user.pivot.task_id) {
assignedUsers[taskId].push(user.pivot.user_id);
}
});
});
});

Javascript polymorphism without OOP classes

In JS or OOP language the polymorhpism is created by making different types.
For example:
class Field {...}
class DropdownField extends Field {
getValue() {
//implementation ....
}
}
Imagine I have library forms.js with some methods:
class Forms {
getFieldsValues() {
let values = [];
for (let f of this.fields) {
values.push(f.getValue());
}
return values;
}
}
This gets all field values. Notice the library doesnt care what field it is.
This way developer A created the library and developer B can make new fields: AutocompleterField.
He can add methods in AutocompleterField withouth changing the library code (Forms.js) .
If I use functional programming method in JS, how can I achieve this?
If I dont have methods in object i can use case statements but this violates the principle. Similar to this:
if (field.type == 'DropdownField')...
else if (field.type == 'Autocompleter')..
If developer B add new type he should change the library code.
So is there any good way to solve the issue in javascript without using object oriented programming.
I know Js isnt exactly OOP nor FP but anyway.
Thanks
JavaScript being a multi-purpose language, you can of course solve it in different ways. When switching to functional programming, the answer is really simple: Use functions! The problem with your example is this: It is so stripped down, you can do exactly the same it does with just 3 lines:
// getValue :: DOMNode -> String
const getValue = field => field.value;
// readForm :: Array DOMNode -> Array String
const readForm = formFields => formFields.map(getValue);
readForm(Array.from(document.querySelectorAll('input, textarea, select')));
// -> ['Value1', 'Value2', ... 'ValueN']
The critical thing is: How is Field::getValue() implemented, what does it return? Or more precisely: How does DropdownField::getValue() differ from AutocompleteField::getValue() and for example NumberField::getValue()? Do all of them just return the value? Do they return a pair of name and value? Do they even need to be different?
The question is therefor, do your Field classes and their inheriting classes differ because of the way their getValue() methods work or do they rather differ because of other functionality they have? For example, the "autocomplete" functionality of a textfield isn't (or shouldn't be) tied to the way the value is taken from it.
In case you really need to read the values differently, you can implement a function which takes a map/dictionary/object/POJO of {fieldtype: readerFunction} pairs:
/* Library code */
// getTextInputValue :: DOMNode -> String
const getTextInputValue = field => field.value;
// getDropdownValue :: DOMNode -> String
const getDropdownValue = field => field.options[field.selectedIndex].value;
// getTextareaValue :: DOMNode -> String
const getTextareaValue = field => field.textContent;
// readFieldsBy :: {String :: (a -> String)} -> DOMNode -> Array String
readFieldsBy = kv => form => Object.keys(kv).reduce((acc, k) => {
return acc.concat(Array.from(form.querySelectorAll(k)).map(kv[k]));
}, []);
/* Code the library consumer writes */
const readMyForm = readFieldsBy({
'input[type="text"]': getTextInputValue,
'select': getDropdownValue,
'textarea': getTextareaValue
});
readMyForm(document.querySelector('#myform'));
// -> ['Value1', 'Value2', ... 'ValueN']
Note: I intentionally didn't mention things like the IO monad here, because it would make stuff more complicated, but you might want to look it up.
In JS or OOP language the polymorhpism is created by making different types.
Yes. Or rather, by implementing the same type interface in different objects.
How can I use Javascript polymorphism without OOP classes
You seem to confuse classes with types here. You don't need JS class syntax to create objects at all.
You can just have
const autocompleteField = {
getValue() {
…
}
};
const dropdownField = {
getValue() {
…
}
};
and use the two in your Forms instance.
Depends on what you mean by "polymorphism". There's the so-called ad-hoc polymorphism which type classes in Haskell, Scala, or PureScript provide -- and this kind of dispatch is usually implemented by passing witness objects along as additional function arguments, which then will know how to perform the polymorphic functionality.
For example, the following PureScript code (from the docs), which provides a show function for some types:
class Show a where
show :: a -> String
instance showString :: Show String where
show s = s
instance showBoolean :: Show Boolean where
show true = "true"
show false = "false"
instance showArray :: (Show a) => Show (Array a) where
show xs = "[" <> joinWith ", " (map show xs) <> "]"
example = show [true, false]
It gets compiled to the following JS (which I shortened):
var Show = function (show) {
this.show = show;
};
var show = function (dict) {
return dict.show;
};
var showString = new Show(function (s) {
return s;
});
var showBoolean = new Show(function (v) {
if (v) {
return "true";
};
if (!v) {
return "false";
};
throw new Error("Failed pattern match at Main line 12, column 1 - line 12, column 37: " + [ v.constructor.name ]);
});
var showArray = function (dictShow) {
return new Show(function (xs) {
return "[" + (Data_String.joinWith(", ")(Data_Functor.map(Data_Functor.functorArray)(show(dictShow))(xs)) + "]");
});
};
var example = show(showArray(showBoolean))([ true, false ]);
There's absolutely no magic here, just some additional arguments. And at the "top", where you actually know concrete types, you have to pass in the matching concrete witness objects.
In your case, you would pass around something like a HasValue witness for different forms.
You could use a the factory pattern to ensure you follow the open close principle.
This principle says "Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification".
class FieldValueProviderFactory {
getFieldValue(field) {
return this.providers.find(p => p.type === field.type).provider(field);
}
registerProvider(type, provider) {
if(!this.providers) {
this.providers = [];
}
this.providers.push({type:type, provider:provider});
}
}
var provider = new FieldValueProviderFactory();
provider.registerProvider('DropdownField', (field) => [ 1, 2, 3 ]);
provider.registerProvider('Autocompleter', (field) => [ 3, 2, 1 ]);
class FieldCollection {
getFieldsValues() {
this.fields = [ { type:'DropdownField',value:'1' }, { type:'Autocompleter',value:'2' } ];
let values = [];
for (let field of this.fields) {
values.push(provider.getFieldValue(field));
}
return values;
}
}
Now when you want to register new field types you can register a provider for them in the factory and don't have to modify your field code.
new Field().getFieldsValues();

How to dynamically generate class method names in an es6 class?

I am trying to figure out if it’s possible to generate method names on an es6 class. Take for example the following example, a Replacer, which runs replacement rules from a ruleset:
let smileyRules = [
{ ascii: ':)', unicode: '😀 ' },
{ ascii: '8)', unicode: '😎 ' }
]
class Replacer {
constructor(rules){
this.rules = rules
}
replace(text, from, to){
this.rules.forEach(rule => text = text.replace(rule[from], rule[to]))
return text
}
}
let smileyizer = new Replacer(smileyRules)
smileyizer.replace(':)', 'ascii', 'unicode')
// "😀 "
smileyizer.replace(':)', 'unicode', 'ascii')
// ":)"
So that does what it’s supposed to, but I would also like to generate convenience methods that would work like this:
smileyizer.ascii2unicode(':)')
which would internally call
smileyizer.replace(':)', 'ascii', 'unicode')
Of course, I would want to enable unicode2ascii as well. (And in fact, the point of this whole thing is that it will be used with rulesets where each rule has perhaps a dozen keys, so that's a lot of convenience methods.)
In my Replacer class, I expect to generate the methods with something akin to:
generate(){
this.rules.map(firstRule =>
this.rules.map(secondRule => {
// somehow create method called firstRule + '2' + secondRule
})
}
}
…and then I would call this from the constructor.
I know it’s possible to create computed properties using bracket notation, but I can't figure out how I would do something equivalent from inside another method.
Solution (thanks #DShook)
Here’s a working generate method:
generate(){
let names = Object.keys(this.rules[0])
names.forEach(firstName =>
names.forEach(secondName => {
let method = firstName + '2' + secondName
this[method] = (text, from, to) => this.replace(text, firstName, secondName)
})
)
}
generate(){
this.rules.map(firstRule =>
this.rules.map(secondRule => {
this[firstRule+"2"+secondRule] = char => this.replace(char, firstRule, secondRule);
});
);
}
However, dynamic methods are a very bad idea...
In your constructor you would just need to dynamically create the functions however you need to like this:
this['firstRule' + '2' + 'secondRule'] = function(text, from, to){
return text;
}

How to preload a Ramda curried function with item of an Array?

I have tagsList which has about 20 tags, and termIds which is an array of up to 3 tag ids.
I'm trying to find the tags that match the ids in termIds in the tagsList, then set their borders. Looking to avoid for loops and object-oriented programming in favor of a functional programming solution using Ramda curry.
A tag in tagsList looks like :
{
term: 'hi',
id: 123
}
And termIds could look like [123, 345, 678]
When I find an id that matches, I give that tag a new key border1:true, border2:true etc...
Goal:
There is a list of tags, I have another array of termIds, goal is to see if any of the tags in the tagsList have an id that matches the termIds. If so give it a border1, if there are 2, then the 2nd gets border2 and finally 3 gets border 3.
What I tried first:
const checkId = _.curry((term_id, tag) => {
if (tag.id === term_id) {
console.log('match found!', tag)
}
});
const matchId = checkId(termIds);
const coloredTags = R.map(matchId, tagsList);
console.log('coloredTags', coloredTags)
return tagsList;
However this did not work because I am preloading the entire termIds array into the checkId function. When instead I want to preload it with the individual items.
Next I tried this which I thought would work but getting a strange error:
const matchId = R.forEach(checkId, termIds);
This seems a reasonable approach:
R.map(tag => {
const index = R.indexOf(tag.id, termIds);
return (index > -1) ? R.assoc('border' + (index + 1), true, tag) : tag
})(tagsList);
//=> [
// {id: 123, term: "hi", border1: true},
// {id: 152, term: "ho"},
// {id: 345, term: "hu", border2: true},
// {id: 72, term: "ha"}
// ]
Although it could probably be made points-free with enough effort, it would likely be much less readable.
You can see this in action on the Ramda REPL.
If you want to make this into a reusable function, you can do it like this:
const addBorders = R.curry((terms, tags) => R.map(tag => {
const index = R.indexOf(tag.id, terms);
return (index > -1) ? R.assoc('border' + (index + 1), true, tag) : tag
})(tags))
addBorders(termIds, tagsList)
(The call to curry is a Ramda habit. It means you can call addBorders(termIds) and get back a reusable function that is looking for the tags. If you don't need that, you can skip the curry wrapper.)
This version is also on the Ramda REPL.
I think pure JS is enough to do it without Ramda. You just need a map :
var tagsList = [{term: 'hi', id: 123}, {term: 'ho', id: 152}, {term: 'hu', id: 345}, {term: 'ha', id: 72}];
var termIds = [123, 345, 678];
var i = 1;
var results = tagsList.map(x => {
if (termIds.indexOf(x.id) !== -1) x["border"+ (i++)] = true;
return x;
});
console.log(results);
Ah just figured it out, I had to curry the logic a 2nd time:
const matchId = R.curry((tag, term_id) => {
if (tag.id === Number(term_id)) {
console.log('match found!', tag)
}
});
const curried = R.curry((termIds, tag) => {
return R.map(matchId(tag), termIds);
});
const coloredTags = R.map(curried(termIds), tagsList);
console.log('coloredTags', coloredTags)
return tagsList;
So at the coloredTags line, a tag from tagsLists goes into the curried(termIds). Ramda functions accept params from right to left.
curried(termIds) is already preloaded with the termIds array. So next in the const curried = line, the termIds array and single tag make it in and the tag gets sent along into the next curried function matchId, also the termIds array is placed in the R.map. Ramda list functions accept the Array of data as the right param.
Finally in matchId I can make my check!
UPDATE
So the above answers the question I asked, about how to curry an item from an Array. However it caused a bug in my app. Since the termIds array could hold up to 3 items, the coloredTags R.map will run up to 3 times and create duplicate tags in my tagsList.
So just for completeness this is how I solved my in problem, much simpler and didn't need to use a double curried function.
const setTagColors = (tagsList, state) => {
const setBorder = (tag) => {
if (tag.id === Number(state.term_id_1)) {
tag.border1 = true;
} else if (tag.id === Number(state.term_id_2)) {
tag.border2 = true;
} else if (tag.id === Number(state.term_id_3)) {
tag.border3 = true;
}
return tag;
};
const coloredTags = R.map(setBorder, tagsList);
return state.term_id_1 ? coloredTags : tagsList;
};

Categories