how can I dynamically get the variables in javascript? - javascript

I am working on React app, but I think this is most likely JavaScript problem. I have many variables with a pattern, VARIABLE_NAME_{number}, example, FOO_1, FOO_2 ... so on. In my function, it takes index as input and return mapped output.
import power from 'customClass';
const getOneOfFoo = (theIndex) => {
power()
.then(data => {
let result = data?.first?.second?.FOO_{theIndex}?.name ; // how can I declare this one with passing input?
// example, if theIndex=59, I want to have
// let result = data?.first?.second?.FOO_59?.name;
resolve({
result: result
});
})
.catch(err => console.log(err))
});
The data object structure is like this,
data
|-first
|-second
|-FOO_1
|-name
|-FOO_2
|-name
|-FOO_3
|-name
|-FOO_4
|-name
...
In line 5, I want to assign result dynamically. How can I achieve this?

You can treat it like a dictionary.
Here's an example:
const data = {
'FOO_1': { name: 'Kenobi' },
'FOO_2': { name: 'Skywalker' },
'FOO_3': { name: 'Yoda' },
'FOO_4': { name: 'Kylo' },
'FOO_5': { name: 'Sidious' }
}
function getVar(index) {
let result = data?.[`FOO_${index}`]?.name;
return result;
}
console.log(getVar(1)); // expected output: Kenobi
console.log(getVar(2)); // expected output: Skywalker
console.log(getVar(3)); // expected output: Yoda
console.log(getVar(4)); // expected output: Kylo
console.log(getVar(5)); // expected output: Sidious
console.log(getVar(6)); // expected output: undefined
So for your case, it would probably be something like this:
import power from 'customClass';
const getOneOfFoo = (theIndex) => {
power()
.then(data => {
let result = data?.first?.second?.[`FOO_${theIndex}`]?.name;
resolve({
result: result
});
})
.catch(err => console.log(err))
});
And btw, resolve doesn't seem to be defined.

let result = data?.first?.second[`FOO_${theIndex}`]?.name ;
u can use [] with inside is string which is name of property

Related

How can I get the value stored in that array 'my_array' Cypress?

cy.get(tableBody)
.each(($el, $index) => {
let myArray = []
let mydata = $el.text()
if($index!==1) {
myArray.push(mydata.trim())
}
}).then((my_list) => {
cy.log(my_list)
Here, my_list print the value same as what cy.get(tableBody) return. It's like cy.get(tableBody).then((my_list) => { cy.log(my_list) }. I want that array and use that later. I know I can get return like this
TableDataBeforeSorting() {
let myArray = []
cy.get(tableBody).each(($el, $index) => {
let mydata = $el.text()
if($index!==1) {
myArray.push(mydata.trim())
}
})
return myArray
But I want it using then so that I can use it later. Any suggestion or feedback will be highly appreciated.
I've found that when Cypress doesn't do what I want in terms of saving data to variables to use later, I can save data to disk:
// save it
cy.XYZcommand().then((results) => {
cy.writeFile('temp.txt', results)
})
...
cy.readFile('temp.txt')
.then((results) => {
// do something with it
})
...
// clean it up
cy.exec('rm temp.txt')

How to simplify function which returns an object?

I have a function which returns an object but I don't like that I gotta declare it first and then do forEach method
export default (data) => {
const keysWithDotsObject = {};
Object.keys(data).forEach((keyWithDot) => {
Object.keys(data[keyWithDot]).forEach((key) => {
keysWithDotsObject[`${keyWithDot}.${key}`] = data[keyWithDot][key];
});
});
return keysWithDotsObject;
};
I think there should be something like this
export default (data) => {
const keysWithDotsObject = Object.keys(data).map((keyWithDot) => {
Object.keys(data[keyWithDot]).map((key) => ({
[`${keyWithDot}.${key}`]: data[keyWithDot][key],
}));
});
return keysWithDotsObject;
};
But for some reason, it doesn't work.
PS: In this part --
[`${keyWithDot}.${key}`]
-- I'm trying to create a key with a name separated by a dot (I don't like that, but that's what back-end wants me to)
Input :
Query1 = {
locus_ids: [25, 26],
microorganism_ids: [12],
};
Output :
Query1.locus_ids: [25, 26],
Query1.microorganism_ids: [12]
I also would like any suggestions on how to write more readable code
Did you consider using reduce?
export default (data) => Object.keys(data).reduce((acc, keyWithDot) => (
Object.keys(data[keyWithDot]).forEach((key) => {
acc[`${keyWithDot}.${key}`] = data[keyWithDot][key];
}),
acc
), {});
You can also use Object.fromEntries, map and flatMap should do the job:
export default (data) =>
Object.fromEntries(
Object.keys(data).flatMap((keyWithDot) =>
Object.keys(data[keyWithDot]).map((key) => [`${keyWithDot}.${key}`, data[keyWithDot][key]])
)
);
First, you build an array for each subentry, for each subentry, you flatten the array you got into an array of key/value, then with Object.fromEntries, you make a new object!
What if the backend decides to add one more nesting? I would choose to go with a recursive function that accounts for that:
function flattenObject(data) {
return Object.fromEntries(
Object.entries(data).flatMap(([key, value]) => {
if (Array.isArray(value) || typeof value !== 'object') {
// The condition might need to be changed depending on the expected data types
return [[key, value]];
}
return Object.entries(flattenObject(value))
.map(([suffix, nestedValue]) => [`${key}.${suffix}`, nestedValue]);
})
)
}
This works even for inputs such as:
{
query1: {
nested: {
test: true
}
},
query2: [1, 2, 3]
}
The above example results in:
{
"query1.nested.test": true,
"query2": [1,2,3]
}

Jest - Object is being re-used across tests

I have a simple function to test. I am using jest, and I am assigning in every it("") statement, an assertion constant, with my ArrayOfObjects.
While the first test passes. All other tests fail, cause they are using the object(assertion) from the first test.
How can I teardown the constant value in every test? Or in general fix it? Here is the code:
describe('Fn: CreateEdges', () => {
it('should RETURN [1 Key]', () => {
const assertion = [
{
identifier: {
name: '1'
}
}
];
const expectation = [
{
key: '1'
}
];
expect(createFn(assertion)).toEqual(expectation);
});
it('should RETURN [2 Keys]', () => {
const assertion = [
{
identifier: {
name: '1',
},
identifier: {
name: '2'
}
}
];
const expectation = [
{
key: '1',
},
{
key: '2',
}
];
expect(createFn(assertion)).toEqual(expectation); // This fails cause it is using the Object from test1
});
});
The createFn below:
import * as R from 'ramda';
function createnFn(array: any[]) {
return R.flatten(
array.map(({ identifier }: any) =>
identifier.key.map((marker: { name: string }) => ({
source: identifier.name,
target: marker.name,
}))
)
);
}
export default R.memoizeWith(R.identity, createFn);
As it was already mentioned, memoization causes the problem, it makes the function stateful. Non-memoized createnFn is local to the module, only memoized version of it is exported. Since it's reused between multiple tests, this results in test cross-contamination.
The solution is to re-import the module. It should be imported in tests not on top level but in tests, re-import is done with jest.isolateModules or jest.resetModules:
beforeEach(() => {
jest.resetModules();
})
it('should RETURN [1 Key]', () => {
const createFn = require('...').default;
...
expect(createFn(assertion)).toEqual(expectation);
});
Ciao, you problem could be related to function memoization.
Basically, Memoization is a technique that stores values in cache to improve performances. Try to remove R.memoizeWith(R.identity, createFn) and problem should disappears.

How can I replace all matches of a string?

I am trying to replace some sort of self made variables from multiple strings in an array.
Later I will make a promp for the user to ask for the replacement, but in the first step I'm just trying to replace it with a 'test' value ('###').
Oh and I want it to be simplified (with the chained functions - I've only just discovered for myself and thats the problem here :D ). Can someone please help me?
What I got so far:
const replaceAll = (obj) => {
obj.cmds.forEach((element, index, array) => {
obj.cmds[index] = obj.cmds[index].match(/{.*?}/gmi).map((value) => {
console.log('valuex', value)
/*
// Here the user should be asked for the replacement in the future.
// const newValue = userPromp(value)
*/
return obj.cmds[index].split(value).join('###')
})
})
return obj
}
const newObj = replaceAll({
name: 'ULTRA TEST',
cmds: [
'port {port}',
'port {port} und random {random}',
'random {random} und value {value}'
]
})
console.log(newObj)
I think what you are looking for is something like this:
You can see that instead of using match, i just use Replace and pass in the regex.
replaceAll = (obj) => {
var m = {};
obj.cmds.forEach((element, index, array) => {
obj.cmds[index].match(/{.*?}/gmi).map( value => {
var r = m[value]
if (!r) { /* Create Prompt here. */ r = Math.random() * 10 }
m[value] = r;
})
Object.keys(m).map( key => {
obj.cmds[index] = obj.cmds[index].replace(key, m[key])
});
})
return obj
}
newObj = replaceAll({
name: 'ULTRA TEST',
cmds: [
'port {port}',
'port {port} und random {random}',
'random {random} und value {value}'
]
})
The json which is returned by newObj is:
{
"name":"ULTRA TEST",
"cmds":[
"port 1",
"port 1 und random 2",
"random 2 und value 3"
]
}
So what will happen is that it will only prompt the user for values not previously prompted for in that iteration of replaceAll.
This will do exactly what you want.
i guess you need something like this
const replaceAll = obj => ({
...obj,
cmds: obj.cmds.map(string => {
let result = string;
string.match(/{.*?}/gmi).forEach(template => {
// build <replace-string> here
result = result.split(template).join('$$$');
});
return result;
})
});
const newObj = replaceAll({
name: 'ULTRA TEST',
cmds: [
'port {port}',
'port {port} und random {random}',
'random {random} und value {value}'
]
})
console.log(newObj)
You can use the inbuilt replace function.
String.replace(/regex here/, <string to replace the matched regex>)
So for your case, I could do:
// take input here
let input = <input from user>
// string to replace with
let replacementString = "###"
for (let cmd of cmds) {
cmd.replace(/`${input}`/gmi, replacementString);
}
Use for of instead of forEach as you cannot execute break in the latter. (Assuming you might want to have functionality of replacing one by one in the future).

Promise.resolve() return only one element in nested array

Here is my code:
search(): Promise<MyModel[]> {
const query = {
'action': 'update',
};
return new Promise((resolve, reject) => {
this.api.apiGet(`${API.SEARCH_STUDENT}`, query).then((data) => {
const a = data.items.map(i => i);
const b = data.items.map(i => i);
console.log(a.array1[0].array2.length); // 1
console.log(b.array1[0].array2.length); // 5
resolve(a);
}, (error) => {
reject(error);
});
});
}
MyModel class:
class MyModel {
...
array1: [{
array2: []
}]
}
data.items[0].array1[0].array2 returned by function apiGet contains 5 elements. But if I put a or b into resolve function, it now just keep first element only like the comments in the snippet.
Could anyone show what I miss here?
Firstly I am not sure why you wrap a promise in a promise.. why do you need to do that when
this.api.apiGet returns a promise.
Also, why are you trying to map? I bet if you console.log(data.items) the same data would come back. I think you have just got a little confused with your code. A tidy up of the code should resolve all of this for you.
I would do something like the below, now every time you call search you get all the data back which you can use when you want it.
search(): Promise<MyModel[]> {
const query = {
'action': 'update',
};
return this.api.apiGet(API.SEARCH_STUDENT, query)
.then((data) => data as MyModel[]));
}

Categories

Resources