I've used hasOwnProperty and typeof in the past but this one is stumping me...
I'm trying to get all the keys that have keys that match so I can pair them with other keys example:
{"meals": [{
strIngredient1 : lemons
strIngredient2 : paprika
strIngredient3 : red onions
strIngredient4 : chicken thighs
strIngredient5 : vegetable oil
strMeasure1 : 2 Juice
strMeasure2 : 4 tsp
strMeasure3 : 2 finely chopped
strMeasure4 : 16 skinnless
strMeasure5 :
}]}
It's apparent that strIngredient1 matches with strMeasure1 etc...
Any suggestions or help would be greatly appreciated!!
Explained
In this example, you can see that I've provided the solution in two parts, one being a simple way to simply access 'x' ingredient from the array of meals, then another solution which will iterate over the array of meals, printing out each individual ingredient.
As I've stated within my solution, you can use forEach or alternatively, you can also use functions such as map or reduce if you wish. In the event that you don't know when to use which, the basic rule of thumb is that you'd use map or reduce if you wish to follow functional programming concepts. The forEach solution allows for side effects to happen more easily, etc... I mean this is debatable to a certain extent, but that's the basic idea anyways...
Edit
I've included a simple log function just for this demo, long story short, when you run this code snippet, personally I find it disgusting how little space is provided for the console window, so log one thing at a time after some delay and clear the console too.
let delay = 0;
const DELAY_INC = 1500;
// Just for this demo, have the ability to log something,
// after a delay and clear the console.
const log = (arg, alrt) => {
setTimeout(() => {
console.clear();
console.log(arg);
if (alrt != null) {
alert(alrt);
}
}, delay);
delay += DELAY_INC;
};
// Your data.
var data = {
"meals": [{
strIngredient1: 'lemons',
strIngredient2: 'paprika',
strIngredient3: 'red onions',
strIngredient4: 'chicken thighs',
strIngredient5: 'vegetable oil',
strMeasure1: '2 Juice',
strMeasure2: '4 tsp',
strMeasure3: '2 finely chopped',
strMeasure4: '16 skinnless',
strMeasure5: ''
}]
};
// Just some demo.
var meals = data.meals;
var meal = meals[0];
var ingredient = meal.strIngredient1;
log(data); // Log the raw data.
log(meals); // Log the array of meals.
log(meal); // Log a specific meal.
log(ingredient); // Log a specific ingredient.
// If you wish to iterate, log each ingredient for each meal.
data.meals.forEach(meal => Object.keys(meal).forEach(key => log(meal[key])));
// Here's a solution.
const newArray = data.meals.reduce((array, meal) => {
// Rather than iterate over ALL of the keys, just
// do this, basically 50% of the keys.
const subArray = Object.keys(meal).filter(key => key.indexOf('strIngredient' == -1));
// Basically add some ojects to the array.
subArray.forEach(key => {
const int = key.replace(/\D/g, '');
const measureKey = `strMeasure${int}`;
const ingredientKey = `strIngredient${int}`;
const obj = {
ingredient: meal[ingredientKey],
measure: meal[measureKey]
};
array.push(obj);
});
// Make sure to return the array.
return array;
}, []);
// Now just print the resuts, and make sure that you know
// and alert that the app has finished.
log(newArray, 'FINISHED');
For those interested or if it helps anyone here is the final product! All neat and tidy in one array, easy to use! :) Thank you again JO3-W3B-D3V!
getRecipe: function(url) {
request({
url: url,
method: 'GET'
}, (error, response, body) => {
if (!error && response.statusCode == 200) {
var result = JSON.parse(body);
//console.log(result);
// Just some TESTING.
var meals = result.meals; //returns array
var meal = meals[0]; // returns object
//console.log(meal);
// Start here to rename keys and match them to the ingredients.
const newArray = meals.reduce((array, meal) => {
// Rather than iterate over ALL of the keys, just
// do this, basically 50% of the keys.
const subArray = Object.keys(meal).filter(key => key.indexOf('strIngredient' == -1));
// console.log(subArray);
// Basically add some ojects to the array.
subArray.forEach(key => {
const int = key.replace(/\D/g, '');
const measureKey = `strMeasure${int}`;
const ingredientKey = `strIngredient${int}`;
const obj = {
measure: meal[measureKey],
ingredient: meal[ingredientKey]
};
// console.log(obj); //Testing data before
if (obj.measure && obj.ingredient != 'undefined' || undefined || "" || null){
array.push(obj);
// console.log(array); //Testing data after
}
});
const recipeName = meal.strMeal;
const instruction = meal.strInstructions;
const video = meal.strYoutube;
const thumb = meal.strMealThumb;
const nation = meal.strArea;
const category = meal.strCategory;
const recipe = {recipeName, instruction, video, thumb, nation, category};
array.push(recipe);
//console.log(recipe); Testing full array
// Make sure to return the array.
return array;
}, []);
// Now just print the resuts, and make sure that you know
// and alert that the app has finished.
console.log(newArray, "FINISHED");
Related
I'll try my best to explain my problem. In my application I have a section that lists all users with a name and image. When adding a new user, the profile picture is taken from an array of default pictures and it has to be different from image of the other users.
Down below there is my solution and it seems to work, but im searching for a cleaner way to do it.
Thank you!
const profileImages = [img1, img2, img3, img4];
let users = [
{
name: "Username1",
image: img1
},
{
name: "Username2",
image: img2
}
];
/*
This array will fill up with the images not already taken by other users,
and I'll randomly pick from these to assign it to the new user
*/
let availableImages = [];
users.forEach(user =>{
if (availableImages.length === 0)
{
availableImages = profileImages.filter(image => image !== user.image);
}
else
{
availableImages = availableImages.filter(image => image !== user.image);
}
});
Use the Array.every() method to check if an image is not used by any user.
let availableImages = profileImages.filter(image => users.every(u => u.image != image));
something like this can work too, but this is a very simplistic approach. you may have some edge cases depending on how these images are assigned if coming from the server-side or something.
// assuming you want to keep a reference to the full list of images
const allImages = [...];
let availableImages = [...allImages];
function getImage() {
const [newImage, ...rest] = availableImages;
availableImages = rest;
return newImage;
}
// use getImage(); when you want to assign a new image
The following code gets you the remaining elements without a second loop. However, programmers must be cautioned that splice() is an expensive operation and it also alters the original array:
profileImages = ['img1', 'img2', 'img3', 'img4'];
users = [
{
name: "Username1",
image: 'img1'
},
{
name: "Username2",
image: 'img2'
}
];
users.map(item => {
let i = profileImages.indexOf(item.image); // get the index
profileImages.splice(i,1); // delete it
});
profileImages; // ['img3', 'img4']
I'm facing strange behaviour of array.includes functionality.
My job is to check if variable feedProduct has all required fields.
function hasAllRequiredFields(requiredFields, feedProduct) {
// Prepare array for adding missing fields
let missing_required = [];
// Print all fields and there type (only for easier debugging)
console.log(feedProduct);
console.log(typeof feedProduct);
feedProduct.forEach(fp => {
console.log(fp)
console.log(typeof fp)
})
// Check if feedProduct array contains all required fields
console.log('')
console.log('CHECK IF ALL REQUIRED FIELDS ARE PRESENT')
requiredFields.forEach(wf => {
console.log(wf);
console.log(typeof wf);
if (!feedProduct.includes(wf)) {
missing_required.push(wf);
}
})
console.log('')
console.log('')
console.log('Missing')
console.log(missing_required)
return missing_required;
}
And the result is so strange. As you can see i'm searching for string 'sku' type of string in array FeedProduct, which includes string 'sku' of type of String, but feedProduct.includes(wf) returns false
I've also tried the same logic with indexOf and result is the same. If I copy/paste this code in Developer tools, then result will be true and no fields will be missing
Can some please point me what else should I check or do, so my code will be working properly. If you need any additional informations, please let me know and I will provide. Thank you
**UPDATE
function is called in some other function, like this
let hasRequiredFields = hasAllRequiredFields(preparedMappingFields.storeFeedMustHaveFields, header);
result for console.log(feedProduct[0], requiredFields[0], feedProduct[0] === requiredFields[0]); is following
sku sku false
** UPDATE TWO
console.log(requiredFields[0].split('').map(c => c.charCodeAt(0)))
console.log(feedProduct[0].split('').map(c => c.charCodeAt(0)))
Log 1:
[ 115, 107, 117 ]
Log 2:
[ 65279, 115, 107, 117 ]
Your are simply looking for the difference between two arrays, so you could as well just do this...
const rf = ['foo', 'bar', 'baz'];
const fp = ['foo', 'baz']
const difference = rf.filter( x => !fp.includes(x))
console.log(difference)
If you would want to get the intersection you would want to do this...
const rf = ['foo', 'bar', 'baz'];
const fp = ['foo', 'baz']
const intersection = rf.filter( x => fp.includes(x))
console.log(intersection)
The problem you have is that the two strings that look like "sku" aren't really equal: the one in the feedProduct array contains an invisible character in front of it.
That character, U+FEFF, also known as the Byte Order Mark is often put at the beginning of Unicode files to indicate the correct byte order (endianness).
I don't know where your data is coming from, but that character was left in it during parsing.
The best way would be to save the file with the data without BOM, or, if that's not possible, remove it just after reading from the file.
To remove this character, you can use the following code:
const outputString = inputString.replace(/^\uFEFF/, '')
The function works.(Although it could be optimised)
function hasAllRequiredFields(requiredFields, feedProduct) {
// Prepare array for adding missing fields
let missing_required = [];
// Print all fields and there type (only for easier debugging)
console.log(feedProduct);
console.log(typeof feedProduct);
feedProduct.forEach(fp => {
console.log(fp)
console.log(typeof fp)
})
// Check if feedProduct array contains all required fields
console.log('')
console.log('CHECK IF ALL REQUIRED FIELDS ARE PRESENT')
requiredFields.forEach(wf => {
console.log(wf);
console.log(typeof wf);
if (!feedProduct.includes(wf)) {
missing_required.push(wf);
}
})
console.log('')
console.log('')
console.log('Missing')
console.log(missing_required)
return missing_required;
}
var feedProductData = [
'sku', 'kategorija_proizvoda', 'naziv_proizvoda',
'proizvodjac', 'specifikacija', 'proizvod_url',
'cena', 'slika_url', 'dostupnost_proizvoda'
];
var requiredFieldData = ['sku', 'required_and_missed'];
hasAllRequiredFields(requiredFieldData, feedProductData);
The only reason I can see is that your sku !== sku. Adding trim should solve the issue.
Optimized code
function hasAllRequiredFieldsUpdated(requiredFieldData, feedProductData) {
return requiredFieldData.filter(x => !feedProductData.includes(x))
}
var feedProductData = [
'sku', 'kategorija_proizvoda', 'naziv_proizvoda',
'proizvodjac', 'specifikacija', 'proizvod_url',
'cena', 'slika_url', 'dostupnost_proizvoda'
];
var requiredFieldData = ['sku', 'required_and_missed'];
var missingFields = hasAllRequiredFieldsUpdated(requiredFieldData, feedProductData);
console.log(missingFields);
Currently I figured out how to call the YGOPRO API in a React function and paste the data inside a stateless component. But now I've come at an impasse when trying to figure out how to use some of the API data to run some float numbers through a process of generating new values to post instead.
Here's the full scenario. I originally made this exact thing in a basic HTML layout because that's what the official YGO forbidden/limited webpage used and what I edited to make a joke video about the f/l list months ago. it still works fairly well and the API displays just fine, but its the matter of using this forloop and math junction code to work in a React environment
This is the getJSON part of the code from the initial HTML project:
$.getJSON(
endpoint + "?startprice=" + startprice + "&endprice=" + endprice,
function (json) {
$.each(json.data, function (ix, obj) {
let cards = [];
let name = obj.name;
let type = obj.type;
let card_sets = obj.card_sets;
let price_array = [];
if (card_sets === undefined) {
return true;
}
for (let i = 0; i < card_sets.length; i++) {
let set_price = parseFloat(card_sets[i].set_price);
if (set_price === 0 || set_price === null || set_price === "0.00") {
continue;
} else {
price_array.push(set_price);
}
}
let min_price = Math.min(...price_array);
let max_price = Math.max(...price_array);
const formatter = new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD",
minimumFractionDigits: 2,
});
let min_price_usd = formatter.format(min_price);
let max_price_usd = formatter.format(max_price);
console.log(name);
console.log(min_price);
What this originally did was simply read each monster entry in the API call within the price values described and grabbed each price value from each set listed within each entry while returning true to any part of the entry that didn't have a valid price value. it would then put all the valid money values in a temp array for that monster entry, use the Math function to get both a minimum and maximum price value from that API and then format them to appear as USD before placing them in the front-end of their respective monster entry.
This is the section of the React function that calls and collects the data for the API, including some throwaway lines that I left in after trying to figure all this out:
export default function TestView() {
// for the Banned cards, or all cards more than $5. The limited section for cards more than $1 will come later
// const name = "Dark Magician";
const startprice = 5.0;
const endprice = 99999.99;
const [data, setData] = useState([]);
// how to apply api names to these values?
let name = data.name;
let type = data.type;
console.log(name);
let price_array = [];
useEffect(() => {
getCardDataByPrice(startprice, endprice)
.then(({ data }) => {
setData(data.data);
})
.catch((error) => console.error(`Error: ${error}`));
}, []);
function getCardDataByPrice(startprice, endprice) {
const ygoproURL = "https://db.ygoprodeck.com/api/v7/cardinfo.php";
let ygoproEndpoint = `${ygoproURL}?startprice=${startprice}&endprice=${endprice}`;
if (startprice) {
ygoproEndpoint += `&startprice=${startprice}`;
}
if (endprice) {
ygoproEndpoint += `&endprice=${endprice}`;
}
return axios.get(ygoproEndpoint);
}
// most of the code used to convert money values of each api entry
// This is where the function that grabs the API values for card set prices was to be gathered and calculated like in the original HTML.
let min_price = Math.min(...price_array);
let max_price = Math.max(...price_array);
const formatter = new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD",
minimumFractionDigits: 2,
});
let min_price_usd = formatter.format(min_price);
let max_price_usd = formatter.format(max_price);
console.log(name);
console.log(min_price);
return (...
...etc.
What I am trying to figure out is how I could grab that API data for each monster entry and start grabbing the cash value from each set like how I used to do it here. I'm assuming its within the function that would call the Endpoint in the first place, but I'm not sure.
tl;dr: trying to port API call code from an HTML site into React is being hampered by not knowing where I can grab and alter the data to stick it to the front-end.
You can find the original HTML source code here to help you understand what I'm trying to do.
https://github.com/patrickfinnigan/yugioh_banlist_manipulation
And you can also find how I'm trying to do the same thing in React, in case you'd need to see more of the overall code.
https://github.com/patrickfinnigan/custom_yugioh_banlists_react
(The Relative path would be 'ygo_banlists\src\views\TestView.js')
I have an object that has multiple keys and each of these keys has an array storing multiple elements. I want to be able to remove a specified element from the key's array.
I have tried using the delete keyword as well as the filter method, but I have been unsuccessful. I'm a total newbie to JS so I appreciate any assistance. Also, I want to do this using ONLY JavaScript, no libraries.
Here is the code where I am creating my object:
function add(task, weekdayDue) {
let capitalWeekday = weekdayDue.charAt(0).toUpperCase() +
weekdayDue.slice(1);
if (toDoList[capitalWeekday] === undefined) {
let subArr = [];
toDoList[capitalWeekday] = subArr.concat(task);
} else {
toDoList[capitalWeekday].push(task);
}
}
and here is the code as I have it now. Clearly it is not producing the correct result:
function remove(task, weekdayDue) {
let capitalWeekday = weekdayDue.charAt(0).toUpperCase() +
weekdayDue.slice(1);
delete toDoList.capitalWeekday[task]
//the below code is working; i want to send this to another
array
if (archivedList[capitalWeekday] === undefined) {
let subArr = [];
archivedList[capitalWeekday] = subArr.concat(task);
} else {
archivedList[capitalWeekday].push(task);
}
};
add('laundry', 'monday');
add('wash car', 'monday');
add ('vacuum', 'tuesday');
add('run errands', 'wednesday');
add('grocery shopping', 'wednesday');
// the output is: { Monday: [ 'laundry', 'wash car' ],
Tuesday: [ 'vacuum' ],
Wednesday: [ 'run errands', 'grocery shopping' ] }
Then let's say I want to remove 'wash car' from Monday I was trying:
remove('wash car', 'monday');
console.log(toDoList)
// The output is an empty object {}
I personally would refactor a bit your code, but I've worked a bit around it to fix some issues.
First of all, you shouldn't use delete for your scenario, because it will reset the item at the nth position of the array with the default value, which is undefined.
Usually, for that kind of operations, since you deal with strings, you rather take a look at the first occurrence of your item in the array, take its index, and use splice (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice) to actually remove the item from the array.
In this way, you end up with a clean array without invalid items in it.
Below is the working code (with the mentioned fixes) that does what you asked. As a side note, I would suggest you to avoid working with strings for such purposes, but I would rather tackle objects with unique ids, so that it's significantly easier to keep track of them between arrays and objects.
Additionally, there are some cases that you didn't think about, for instance I can think about calling remove by giving an invalid task, so you may work a bit around the code below to handle the case where taskIndex is -1 (meaning that no item was found with that index).
var toDoList = {}, archivedList = {};
function add(task, weekdayDue) {
let capitalWeekday = weekdayDue.charAt(0).toUpperCase() + weekdayDue.slice(1);
if (toDoList[capitalWeekday] === undefined) {
let subArr = [];
toDoList[capitalWeekday] = subArr.concat(task);
} else {
toDoList[capitalWeekday].push(task);
}
}
function remove(task, weekdayDue) {
let capitalWeekday = weekdayDue.charAt(0).toUpperCase() + weekdayDue.slice(1);
let taskIndex = toDoList[capitalWeekday].indexOf(task);
toDoList[capitalWeekday].splice(taskIndex, 1);
//delete toDoList[capitalWeekday][taskIndex];
if (archivedList[capitalWeekday] === undefined) {
let subArr = [];
archivedList[capitalWeekday] = subArr.concat(task);
} else {
archivedList[capitalWeekday].push(task);
}
};
add('test', 'monday');
add('wash car', 'monday');
remove('wash car', 'monday');
console.log(toDoList);
console.log(archivedList);
You are on the right path. Maybe the trouble you had with filter is because filter will return a new Array and not modify the current one. You could update your remove function and replace the line:
delete toDoList.capitalWeekday[task]
with
toDoList.capitalWeekday = toDoList.capitalWeekday.filter((item) => {return item !== task});
function remove(task, weekdayDue) {
let capitalWeekday = weekdayDue.charAt(0).toUpperCase() +
weekdayDue.slice(1);
// Assign new array with all elements but task
toDoList[capitalWeekday] = toDoList[capitalWeekday].filter(i => i !== task)
};
add('foo'...
add('bar'...
"{
"Baz": [
"Foo",
"Bar"
]
}"
remove('foo'...
"{
"Baz": [
"Bar"
]
}"
I have a program that pushes values into one data structure like this:
if(symbolType == "C" || symbolType == "P") // The calls and puts
stocks.push({
symbol: symbol,
undsymbol: undSymbol,
open: 0,
type: symbolType,
expiry: expiry,
days: days,
strike: strike
});
}
else // The stock
{
stocks.push({
symbol: symbol,
open: 0,
type: symbolType
});
}
So this is the key: NOT A STRING!
{
symbol: symbol,
open: 0,
type: symbolType
}
And the values of which are many look like this:
{
symbol: symbol,
undsymbol: undSymbol,
open: 0,
type: symbolType,
expiry: expiry,
days: days,
strike: strike
}
The problem is that stocks and calls and puts are being put into one collection. Instead, I want to add the the stocks and their corresponding calls and puts into a dictionary/map, where the stocks are the keys, and the calls and puts get pushed into an array indexed by it's stock.
At the end, I want to be able to iterate and get the keys and values.
How do I declare this object
Index into it to see if the key[stock] already exists, if it doesn't add it with an empty array.
If I get a "C" or "P", I want to get the corresponding array that holds the Calls/Puts for this key [stock] and push the call/put into the array.
Initially I thought the declaration was something like this:
var stockCallsPutDict = {[]}
stockCallsPutDict[stock] = [];
stockCallsPut[stock].push(call);
// Pretty print the dict of keys and its options =
stockCallsPutDict.forEach(function kvp) {
...
}
If ES6 is an option, you can either build an object yourself or use a Map.
Here's some quick code I came up with:
const stocks = {};
const addCallAndPut = callAndPut => {
const symbol = callAndPut.symbol;
if (!stocks[symbol]) {
stocks[symbol] = [];
}
stocks[symbol].push(callAndPut);
}
const showStuff = () => {
for (const symbol in stocks) {
// output stuff using stocks[symbol]
}
}
OR WITH A MAP
const stocks = new Map();
// basic implementation
const addCallAndPut = callAndPut => {
const stockCallsAndPuts = stocks.get(callAndPut.symbol) || [];
stockCallsAndPuts.push(callAndPut);
stock.set(callAndPut.symbol, stockCallsAndPuts);
}
There are a few ways to go about this, and the best depends on how the data needs to be processed later, but from your description I'd go with something along the lines of
var stocks = {};
var stockCallsPut = {};
// loop over stocks and actions
if (!(symbol in stocks)) {
stocks[symbol] = [];
}
if (!(symbol in stockCallsPut)) {
stockCallsPut[symbol] = {};
}
if (!(symbolType in stockCallsPut[symbol])) {
stockCallsPut[symbol][symbolType] = [];
}
// accumulated stock json items here
stocks[symbol].push(new_stock_item);
// accumulated push/call json items of stock here
stockCallsPut[symbol][symbolType].push(new_action);
I'm still not sure I actually understood what your data looks like, but sounds kind of like this to me:
// Not sure if data is an object or array
var data = {
'one': {
'name': 'one-somename',
'number': 'one-somenumber',
'symbol': 'C'
},
'two': {
'name': 'two-somename',
'number': 'two-somenumber',
'symbol': 'P'
},
'three': {
'name': 'three-somename',
'number': 'three-somenumber',
'symbol': 'C'
}
};
var stocks = {};
for (var name in data) {
// It sounded like you wanted a call/put array for each object but I'm not sure if that's true since it wouldn't be possible... if so can just divide this part up into it's appropriate place in the if statement below
// Checking that the property is set on the object, if it is, it uses itself, otherwise it adds it with the call/put arrays created
stocks[name] = stocks[name] ? stocks[name] : {'calls': [], 'puts': []};
var type;
if (data[name]['symbol'] === 'C') {
type = 'calls';
} else if (data[name]['symbol'] === 'P') {
type = 'puts';
}
stocks[name][type].push(data[name]);
}