Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 3 years ago.
Improve this question
How this code can be improved or optimized. Though it gives the required result. I want to write it with a better approach.
I am fetching data from www.contentful.com and then filtring it to get the localization with their keys. Note we have to use .reduce() function it is a requirement
import * as fs from 'fs';
const client = createClient();
interface IEntry {
id: string;
text: string;
}
interface IEntries {
[key: string]: { [key: string]: string };
}
export async function getLocalization() {
const entries = await client.getEntries<IEntry>({
skip: 0,
limit: 100,
locale: '*',
content_type: 'translation',
});
let enEntries: IEntries = entries.items
.map((e: any) => e.fields)
.reduce(
(ret, entry) => ({
...ret,
[entry.id.fi]: entry.text.en,
}),
{},
);
let fiEntries: IEntries = entries.items
.map((e: any) => e.fields)
.reduce(
(ret, entry) => ({
...ret,
[entry.id.fi]: entry.text.fi,
}),
{},
);
let svEntries: IEntries = entries.items
.map((e: any) => e.fields)
.reduce(
(ret, entry) => ({
...ret,
[entry.id.fi]: entry.text.sv,
}),
{},
);
const translations = {
['en']: { ...enEntries },
['fi']: { ...fiEntries },
['sv']: { ...svEntries },
};
const dir = './data';
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir);
}
fs.writeFileSync('data/translations.json', JSON.stringify(translations));
return true;
}
getLocalization();
Output can be found on this link (actual values have been removed): https://imgur.com/k3rzxWx
Your code looks quite fine, you can however add a helper to save the repetition:
// Can be replaced with Object.fromEntries(values.map(getDescriptor)) soon
function mapOf<E, V>(values: Array<E>, getDescriptor: (entry: E) => [string, V]): { [key: string]: V } {
const result = {};
for(const entry of values) {
const [key, value] = getDescriptor(entry);
result[key] = value;
}
return result;
}
const translations = {
en: mapOf(entries.items, it => ([it.fields.id.fi, it.fields.text.en])),
fi: mapOf(entries.items, it => ([it.fields.id.fi, it.fields.text.fi])),
sv: mapOf(entries.items, it => ([it.fields.id.fi, it.fields.text.sv])),
};
Or as another approach you could fill your translations object automatically when iterating the entries:
const translations = { en: {}, fi: {}, sv: {}, };
for(const it of entries.items) {
const key = it.fields.id.fi, values = it.fields.text;
for(const language in values) {
if(!translations[language]) continue;
translations[language][key] = values[language];
}
}
i think the following approach will be best in this case.
import { IFields, ITranslation } from './localization.types';
import * as fs from 'fs';
const client = createClient();
const en: ITranslation = {};
const fi: ITranslation = {};
const sv: ITranslation = {};
export async function getLocalization() {
const entries = await client.getEntries<IFields>({
skip: 0,
limit: 100,
locale: '*',
content_type: 'translation',
});
const localizations = entries.items
.map(e => e.fields)
.reduce(
(ret, entry) => {
ret.en[entry.id.fi] = entry.text.en === undefined ? 'no_value_found' : entry.text.en;
ret.fi[entry.id.fi] = entry.text.fi === undefined ? 'no_value_found' : entry.text.fi;
ret.sv[entry.id.fi] = entry.text.sv === undefined ? 'no_value_found' : entry.text.sv;
return ret;
},
{ en, fi, sv },
);
const dir = './data';
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir);
}
fs.writeFileSync('data/translations.json', JSON.stringify(localizations));
return true;
}
getLocalization();
And following are the types
export interface IId {
fi: string;
}
export interface IText {
en: string;
fi: string;
sv: string;
}
export interface IFields {
id: IId;
text: IText;
}
export interface ITranslation {
[key: string]: string;
}
Related
I have the following code I am working with this API https://developer.mozilla.org/en-US/docs/Web/API/Performance/measureUserAgentSpecificMemory. The class is stripped right back
export class Memory {
private stopped = false
private isUserAgentSpecificMemorySupported = true
public memoryData: any = []
constructor() {}
public startMonitoring(): () => void {
if (this.isUserAgentSpecificMemorySupported) {
this.scheduleMeasurement()
}
return () => {
this.stopped = true
}
}
private async performMeasurement(): Promise<void> {
const memory = await (window.performance as any).measureUserAgentSpecificMemory()
const type = memory.breakdown.filter((e: any) => e.types.includes('JavaScript'))
this.memoryData.push(type[0].bytes)
}
}
Jest file.
import {Memory} from './memory'
type UserAgentSpecificMemoryBreakdown = {
bytes: number
types: Array<string>
}
type UserAgentSpecificMemory = {
bytes: number
breakdown: Array<UserAgentSpecificMemoryBreakdown>
}
type MockWindow = {
crossOriginIsolated?: boolean
performance: {
measureUserAgentSpecificMemory?: () => Promise<UserAgentSpecificMemory>
}
}
const data = {
bytes: 1500,
breakdown: [
{
bytes: 1000000,
types: ['JavaScript'],
},
{
bytes: 0,
types: ['DOM'],
},
],
}
describe('Test Memory Class', () => {
let mockWindow: MockWindow
let windowSpy: jest.SpyInstance
beforeEach(() => {
windowSpy = jest.spyOn(window, 'window', 'get')
mockWindow = {
...window,
performance: {
measureUserAgentSpecificMemory: jest.fn(() => Promise.resolve(data)),
},
}
windowSpy.mockImplementation(() => mockWindow)
})
afterEach(() => {
windowSpy.mockRestore()
})
it('should measure User Agent Specific Memory', async () => {
let memory = new Memory()
memory.startMonitoring()
expect(memory.memoryData).toEqual([1000000])
})
})
I am not sure how to make the test file await for the value in the test?
Any help would be great.
window is an object and if it doesn’t contain window function, you can not spy on it.
For your production code, just mock measureUserAgentSpecificMemory function are enough:
import { Memory } from './memory'
describe('Memory', () => {
const data = {
bytes: 1500,
breakdown: [
{
bytes: 1000000,
types: ['JavaScript'],
},
{
bytes: 0,
types: ['DOM'],
},
],
};
let memory: Memory;
let measureUserAgentSpecificMemory: jest.Mock;
beforeEach(() => {
measureUserAgentSpecificMemory = jest.fn().mockResolvedValue(data);
(window as any).performance = {
measureUserAgentSpecificMemory,
};
memory = new Memory();
});
it('should measure User Agent Specific Memory', async () => {
memory.startMonitoring();
expect(memory.memoryData).toEqual([1000000]);
expect(measureUserAgentSpecificMemory).toHaveBeenCalled();
});
});
I am new to typescript and I'm trying to understand why I'm getting the following errors.
Type 'FindCursor<WithId>' is missing the following properties from type 'Topping': name, priceCents
and
Argument of type 'Promise' is not assignable to parameter of type 'void'
public async getToppingsData(ID: String): Promise<Topping> {
const data = await this.collection.find({ _id: ID });
if (data === null || data === undefined) {
throw new Error(`Could not find ${ID}`);
}
const toppingData: Topping = data;
return toToppingObject(toppingData);
}
public async getToppingsByIds(toppingIds: String[]): Promise<String[]> {
toppingIds.map((id) => {
const toppingArray = [];
toppingArray.push(this.getToppingsData(id));
const toppingNames = toppingArray.map((topping) => {
toppingNames.push(topping);
});
return toppingNames;
});
}
here is the toPizzaObject function
import { Document } from 'mongodb';
import { Pizza } from '../application/providers/pizzas/pizza.provider.types';
import { ObjectId } from 'bson';
interface PizzaDocument extends Document, Omit<Pizza, 'toppings' | 'id'> {
toppingIds: ObjectId[];
}
const toPizzaObject = (pizza: PizzaDocument): Pizza => {
return {
id: pizza._id.toHexString(),
name: pizza.name,
description: pizza.description,
toppings: pizza.toppingIds.map((toppingId) => toppingId.toHexString()),
imgSrc: pizza.imgSrc,
};
};
export { PizzaDocument, toPizzaObject };
You messed up your map(), your were returning nothing, hence the void. Also you were not handle the Promise correctly, the return type wasn't matching.
Here is a suggestion:
public async getToppingsByIds(toppingIds: String[]): Promise<String[]> {
const toppingArray = [];
for (let id of toppingIds) {
const data = await this.getToppingsData(id);
toppingArray.push(data);
}
const toppingNames = toppingArray.map((topping) => {
return topping
});
return toppingNames
}
PS: await can't be used on map/forEach(), that's why there is a traditional for-loop.
I managed to write code which does what I need, but I want to make it clean and remove mutation of realtiveGroups.push() but don't know how to achieve it.
How to remove mutation from this code?
export interface OfferCategory {
id: string;
name: string;
}
export interface OfferGroup {
groupId: string;
dependsOn: OfferCategory;
name: string;
rule: Rule;
products: PosProducts[];
}
function relativesFromSubscription(groups: OfferGroup[], dependingGroups: OfferGroup[]): OfferGroup[] {
const relativeGroups: OfferGroup[] = [];
groups.forEach(group => {
if (dependingGroups.some(dependingGroup => group?.dependsOn?.id === dependingGroup.groupId)) {
relativeGroups.push(group);
}
if (relativeGroups.some(relativeGroup => group?.dependsOn?.id === relativeGroup.groupId)) {
relativeGroups.push(group);
}
});
return relativeGroups;
}
Instead of doing everything in one cycle try dividing it into a few:
function relativesFromSubscription(groups: OfferGroup[], dependingGroups: OfferGroup[]): OfferGroup[] {
const groups1 = groups.filter(group => dependingGroups.some(dependingGroup => group?.dependsOn?.id === dependingGroup.groupId));
const groups2 = groups.filter(group => groups1.some(relGroup=> group?.dependsOn?.id === relGroup.groupId));
return [...groups1, ...groups2];
}
Using your code and Array.filter
const relativeGroups: OfferGroup[] = groups.filter(group => {
return dependingGroups.some(dependingGroup => group?.dependsOn?.id === dependingGroup.groupId) || relativeGroups.some(relativeGroup => group?.dependsOn?.id === relativeGroup.groupId)
});
Or if you want the code to be more readable you can add descriptive variables:
const relativeGroups: OfferGroup[] = groups.filter(group => {
const hasDependingGroups = dependingGroups.some(dependingGroup => group?.dependsOn?.id === dependingGroup.groupId);
const hasRelativeGroups = relativeGroups.some(relativeGroup => group?.dependsOn?.id === relativeGroup.groupId)
return hasDependingGroups || hasRelativeGroups
});
I learn react and js myself. please explain why this situation occurs. PS: excuse me for the large text, I tried to explain the problem as clearly as possible. thanks. Essence of the matter: set the initial state through the hook:
const [pokemon, setPokemon] = useState({
img: "",
name: "",
types: [],
abilities: [],
moveList: [],
weight: "",
height: "",
description: "",
genus: "",
chanceToCatch: "",
evolutionURL: ""
});
further I make api requests to get information from inside useEffect:
useEffect(() => {
const fetchData = async () => {
await Axios({
method: "GET",
url: urlPokemonAPI
})
.then(result => {
const pokemonResponse = result.data;
/* Pokemon Information */
const img = pokemonResponse.sprites.front_default;
const name = pokemonResponse.name;
const weight = Math.round(pokemonResponse.weight / 10);
const height = pokemonResponse.height / 10;
const types = pokemonResponse.types.map(type => type.type.name);
const abilities = pokemonResponse.abilities.map(
ability => ability.ability.name
);
const moveList = pokemonResponse.moves.map(move => move.move.name);
setPokemon(() => {
return {
img: img,
name: name,
weight: weight,
types: types,
abilities: abilities,
moveList: moveList,
height: height
};
});
})
await Axios({
method: "GET",
url: urlPokemonSpecies
}).then(result => {
let description = "";
result.data.flavor_text_entries.forEach(flavor => {
if (flavor.language.name === "en") {
description = flavor.flavor_text;
}
});
let genus = "";
result.data.genera.forEach(genera => {
if (genera.language.name === "en") {
genus = genera.genus;
}
});
const evolutionURL = result.data.evolution_chain.url;
const eggGroups = result.data.egg_groups.map(
egg_group => egg_group.name
);
const chanceToCatch = Math.round(
(result.data.capture_rate * 100) / 255
);
setPokemon(pokemon => {
return {
...pokemon,
description: description,
genus: genus,
chanceToCatch: chanceToCatch,
evolutionURL: evolutionURL,
eggGroups: eggGroups
};
});
});
};
fetchData();
}, [urlPokemonAPI, urlPokemonSpecies]);
The problem arises specifically with eggGroups (with identical handling of abilities and types there is no such problem). And this is what happens when I want to output data to a page as <div> Egg Group: {pokemon.eggGroups} </div> the data is displayed normally, but as soon as I want to output eggGroups as well as abilities and types separated by commas (join ( ',')) - error: TypeError: pokemon.eggGroups is undefined. I decided to check this matter through the console and stuffed this eggGroups key into the timeout:
At some point, eggGroups becomes undefined ... why, I can’t understand. But if I set the state separately, like const [egg, setEgg] = useState ([]); setEgg (eggGroups); such a problem is not observed. why is this happening? everything was fine with types and abilities. Thank you in advance.
state updater from hooks doesn't merge the state values when updating state, instead it just replaces the old value with new one
Since you use state updater like
setPokemon(() => {
return {
img: img,
name: name,
weight: weight,
types: types,
abilities: abilities,
moveList: moveList,
height: height
};
});
eggGroups property is lost and hence it becomes undefined. You need to update it by spreading the previous state values obtained from callback
setPokemon((prev) => {
return {
...prev
img: img,
name: name,
weight: weight,
types: types,
abilities: abilities,
moveList: moveList,
height: height
};
});
Your code have a problem, this is the proper way to do await with axios,
you need to import axios like this
import axios from 'axios';
the await should be call with a promise, then it return the data from api like this:
const result = await axios.get(urlPokemonAPI);
This is the code snippet with the same logic to your code
useEffect(() => {
const fetchData = async () => {
// import axios from 'axios';
try {
const result = await axios.get(urlPokemonAPI);
const pokemon = result.data;
setPokemon({
img: pokemon.sprites.front_default,
name: pokemon.name,
weight: Math.round(pokemon.weight / 10),
types: pokemon.types.map(i => i.type.name),
abilities: pokemon.abilities.map(i => i.ability.name),
moveList: pokemon.moves.map(i => i.move.name),
height: pokemon.height / 10
});
const result2 = await axios.get(urlPokemonSpecies);
const data = result2.data;
let description = "";
data.flavor_text_entries.forEach(i => {
const lang = i.language.name
if (lang === "en") {
description = i.flavor_text;
}
});
let genus = "";
data.genera.forEach(i => {
const lang = i.language.name;
if (lang === "en") {
genus = i.genus;
}
});
setPokemon(pokemon => {
return {
...pokemon,
description,
genus,
chanceToCatch: Math.round((data.capture_rate * 100) / 255),
evolutionURL,
eggGroups: data.egg_groups.map(g => g.name)
};
});
} catch (e) {
console.log(e);
}
};
fetchData();
}, [urlPokemonAPI, urlPokemonSpecies]);
do you see another problem: you call setPokemon two times, let's rewrite it again:
useEffect(() => {
const fetchData = async () => {
// import axios from 'axios';
try {
const result = await axios.get(urlPokemonAPI);
const data1 = result.data;
const result2 = await axios.get(urlPokemonSpecies);
const data2 = result2.data;
function resolveDescription(data) {
let description = "";
data.flavor_text_entries.forEach(i => {
const lang = i.language.name
if (lang === "en") {
description = i.flavor_text;
}
});
return description;
}
function resolveGenus(data) {
let genus = "";
data.genera.forEach(i => {
const lang = i.language.name;
if (lang === "en") {
genus = i.genus;
}
});
return genus;
}
setPokemon({
img: data1.sprites.front_default,
name: data1.name,
weight: Math.round(data1.weight / 10),
types: data1.types.map(i => i.type.name),
abilities: data1.abilities.map(i => i.ability.name),
moveList: data1.moves.map(i => i.move.name),
height: data1.height / 10,
description: resolveDescription(data2),
genus: resolveGenus(data2),
chanceToCatch: Math.round((data2.capture_rate * 100) / 255),
evolutionURL: data2.evolution_chain.url,
eggGroups: data2.egg_groups.map(g => g.name)
});
} catch (e) {
console.log(e);
}
};
fetchData();
}, [urlPokemonAPI, urlPokemonSpecies]);
I have an array called combinedMarkets which is either a combination of 5 or 3 different market arrays. All these arrays have the following interface IMarketAsset[]:
export interface IMarketAsset {
exchange: string;
base: string;
quote: string;
price_quote: string;
timestamp: string;
}
Here is where the typescript error occurs:
const combinedMarkets = asset !== 'BTC' && asset !== 'ETH' ?
btcMarkets.concat(ethMarkets).concat(marketUSD).concat(marketUSDC).concat(marketUSDT) :
marketUSD.concat(marketUSDC).concat(marketUSDT);
const filteredMarkets = combinedMarkets.length > 0 ? filterByUSDbase(asset, combinedMarkets) : [];
Argument of type '({ price_quote: string; exchange: string; base: string; quote: string; timestamp: string; } | undefined)[]' is not assignable to parameter of type 'IMarketAsset[]'.
Type '{ price_quote: string; exchange: string; base: string; quote: string; timestamp: string; } | undefined' is not assignable to type 'IMarketAsset'.
Type 'undefined' is not assignable to type 'IMarketAsset'.ts(2345)
const combinedMarkets: ({
price_quote: string;
exchange: string;
base: string;
quote: string;
timestamp: string;
} | undefined)[]
Why is combinedMarkets an array of either an object of type IMarketAsset or undefined?
Full combineExchangeData Function
// Filter by BTC, ETH, USD, USDT or USDC prices
// If asset has BTC/ETH pairing, obtain exchange BTC/ETH price to calculate assets USD/USDT value
export const combineExchangeData =
(asset: string, { marketBTC, marketETH, marketUSD, marketUSDT, marketUSDC }: IGetMarketsRes) => {
const btcBasedExchanges = marketBTC.filter((market: IMarketAsset) => market.base === asset);
const ethBasedExchanges = marketETH.filter((market: IMarketAsset) => market.base === asset);
const btcUSDTprices = marketUSDT.filter((market: IMarketAsset) => market.base === 'BTC');
const btcUSDprices = marketUSD.filter((market: IMarketAsset) => market.base === 'BTC');
const ethUSDTprices = marketUSDT.filter((market: IMarketAsset) => market.base === 'ETH');
const ethUSDprices = marketUSD.filter((market: IMarketAsset) => market.base === 'ETH');
const btcPricedMarkets = filterByExchangeBase(btcBasedExchanges, btcUSDTprices, btcUSDprices);
const ethPricedMarkets = filterByExchangeBase(ethBasedExchanges, ethUSDTprices, ethUSDprices);
const btcMarkets = btcPricedMarkets.filter((market) => R.not(R.isNil(market)));
const ethMarkets = ethPricedMarkets.filter((market) => R.not(R.isNil(market)));
const combinedMarkets = asset !== 'BTC' && asset !== 'ETH' ?
btcMarkets.concat(ethMarkets).concat(marketUSD).concat(marketUSDC).concat(marketUSDT) :
marketUSD.concat(marketUSDC).concat(marketUSDT);
console.log('combinedMarkets', combinedMarkets);
const filteredMarkets = combinedMarkets.length > 0 ? filterByUSDbase(asset, combinedMarkets) : [];
console.log('filteredMarkets', filteredMarkets);
if (R.isEmpty(filteredMarkets)) return [];
return filteredMarkets.map((market: IMarketAsset) => {
if (market) {
return {
...market,
price_quote: formatPrice(market.price_quote)
}
}
});
};
Util functions
Here are the 2 other util functions I use in the main function. Also I have narrowed down the problem to the btcMarkets and ethMarkets arrays. So looking at filterByExchangeBase.
import * as R from 'ramda'
import { USD_CURRENCIES } from '../shared/constants/api'
import { IMarketAsset } from '../shared/types'
const calculateBasePrice = (assetBtcPrice: string | number, btcPrice: string | number) =>
(Number(assetBtcPrice) * Number(btcPrice)).toString();
export const filterByExchangeBase =
(exchanges: IMarketAsset[], usdtExchanges: IMarketAsset[], usdExchanges: IMarketAsset[]) =>
exchanges.map((exchange) => {
let basePriced = usdtExchanges.filter((btcExchange) => btcExchange.exchange === exchange.exchange)[0];
if (!basePriced) {
basePriced = usdExchanges.filter((btcExchange) => btcExchange.exchange === exchange.exchange)[0];
}
if (basePriced) {
const { price_quote: assetBtcPrice } = exchange;
const { price_quote: btcPrice } = basePriced;
return {
...exchange,
price_quote: calculateBasePrice(assetBtcPrice, btcPrice)
}
}
});
export const filterByUSDbase = (asset: string, combinedMarkets: IMarketAsset[] | undefined) => {
if (!combinedMarkets) return [];
return R.not(R.any(R.equals(asset))(USD_CURRENCIES))
? combinedMarkets.filter((marketAsset: IMarketAsset) => {
if (marketAsset && marketAsset.base) {
return marketAsset.base === asset;
}
}) : [];
}
Why is combinedMarkets an array either and object of type IMarketAsset or undefined
Because any of the marketXXX arrays, is an array of IMarketAsset | undefined (instead of just IMarketAsset.
Try defining the type as an array for each option, instead of combining them:
const combinedMarkets: {
price_quote: string;
exchange: string;
base: string;
quote: string;
timestamp: string;
}[] | undefined[] = [];
The problem was that in my filterByExchangeBase util function, I used .map instead of .filter which would result in some undefined objects in that array. Switching to filter made sure that only existing items would make it into the array.
...
Update: By changing .map to .filter, the price_quote updates didn't take
Refactored the logic to make sure that btcMarkets and ethMarkets aren't used if they will be empty.
export const combineExchangeData =
(asset: string, { marketBTC, marketETH, marketUSD, marketUSDT, marketUSDC }: IGetMarketsRes) => {
const btcBasedExchanges = marketBTC.filter((market: IMarketAsset) => market.base === asset);
const ethBasedExchanges = marketETH.filter((market: IMarketAsset) => market.base === asset);
const btcUSDTprices = marketUSDT.filter((market: IMarketAsset) => market.base === 'BTC');
const btcUSDprices = marketUSD.filter((market: IMarketAsset) => market.base === 'BTC');
const ethUSDTprices = marketUSDT.filter((market: IMarketAsset) => market.base === 'ETH');
const ethUSDprices = marketUSD.filter((market: IMarketAsset) => market.base === 'ETH');
const btcPricedMarkets = notBTCorETH(asset) ? filterCryptoBase(btcBasedExchanges, btcUSDTprices, btcUSDprices) : [];
const ethPricedMarkets = notBTCorETH(asset) ? filterCryptoBase(ethBasedExchanges, ethUSDTprices, ethUSDprices) : [];
const btcMarkets = R.not(R.isEmpty(btcPricedMarkets)) ? btcPricedMarkets.filter((market: IMarketAsset) => R.not(R.isNil(market))) : [];
const ethMarkets = R.not(R.isEmpty(ethPricedMarkets)) ? ethPricedMarkets.filter((market: IMarketAsset) => R.not(R.isNil(market))) : [];
const combinedMarkets = notBTCorETH(asset) ?
btcMarkets.concat(ethMarkets).concat(marketUSD).concat(marketUSDC).concat(marketUSDT) :
marketUSD.concat(marketUSDC).concat(marketUSDT);
const filteredMarkets = filterByUSDbase(asset, combinedMarkets);
if (R.isEmpty(filteredMarkets)) return [];
return filteredMarkets.map((market: IMarketAsset) => ({
...market,
price_quote: formatPrice(market.price_quote)
}));
};
export const filterCryptoBase =
(exchanges: IMarketAsset[] | any, usdtExchanges: IMarketAsset[], usdExchanges: IMarketAsset[]) => {
return exchanges.map((exchange: IMarketAsset) => {
let basePriced = usdtExchanges.filter((btcExchange) => btcExchange.exchange === exchange.exchange)[0];
if (!basePriced) {
basePriced = usdExchanges.filter((btcExchange) => btcExchange.exchange === exchange.exchange)[0];
}
if (exchange && basePriced && exchange.price_quote && basePriced.price_quote) {
const { price_quote: assetBtcPrice } = exchange; // Asset price in BTC/ETH
const { price_quote: usdPrice } = basePriced; // BTC/ETH price in USDT/USD
const price_quote = calculateBasePrice(assetBtcPrice, usdPrice).toString();
return {
...exchange,
price_quote
}
}
return null;
});
}