I am doing some operation on list of dictionaries and at the end want to save the data in the form of csv in browser's local storage.
My list of dictionaries data looks like this:
[{
arrivalAddress: "Address"
arrivalLat: "72.989506"
arrivalLon: "19.149307"
arrivalTimeEnd: "8/8/2020 13:37"
arrivalTimeStart: "8/8/2020 13:37"
carrier: "ABC"
departureAddress: "abc,sdkfnv"
departureLat: "73.008304"
departureLon: "19.114599"
departureTime: "8/8/2020 13:37"
hazardousGoods: "TRUE"
id: "1"
"receiver ": "Tom"
sender: "Jack"
tourID: "abc"
vehicleClass: "eu_heavy"
},
arrivalAddress: "Address"
arrivalLat: "72.989506"
arrivalLon: "19.149307"
arrivalTimeEnd: "8/8/2020 13:37"
arrivalTimeStart: "8/8/2020 13:37"
carrier: "ABC"
departureAddress: "abc,sdkfnv"
departureLat: "73.008304"
departureLon: "19.114599"
departureTime: "8/8/2020 13:37"
hazardousGoods: "TRUE"
id: "1"
"receiver ": "Tom"
sender: "Jack"
tourID: "abc"
vehicleClass: "eu_heavy"
},
......
]
And I want it to save in browser's local storage like this:
Can anyone help me to convert list of dicts to csv?
I think this should do it.
const listOfDicts = [...];
const dictionaryKeys = Object.keys(listOfDicts[0]);
const dictValuesAsCsv = listOfDicts.map(dict => (
dictionaryKeys.map(key => dict[key]).join(',')
));
const result = [dictionaryKeys.join(','), ...dictValuesAsCsv].join('\n');
Mind you, the order of the keys might not be correct, for that you should first sort dictionaryKeys to be in the correct order (if the keys are constant, you can replace const dictionaryKeys = Object.keys(listOfDicts[0]); with const dictionaryKeys = ['key1', 'key2', ...];)
Edit:
To answer your question about commas in the values, here's the change you should make
const dictValuesAsCsv = listOfDicts.map(dict => (
dictionaryKeys.map(key => {
if (dict[key].includes(',')) {
return `"${dict[key]}"`;
}
return dict[key];
}).join(',')
));
Explanation: the only difference is that you don't simply return dict[key], but rather you first check if it contains a comma; if it does, you wrap it in quotes, else it stays as before.
Related
I have a question about arrays. I'm new to javascript and I'm writing a program that has a function where it filters 20 elements of an array by category. That is, I have 3 buttons where, by clicking on one of them, the function is turned on and it starts displaying the filtered elements. Please tell me how can this be done? I have already tried a bunch of ways, but in the end nothing happened, although I think that I made a mistake somewhere.
array:
window.products = [
{
id: "i8",
title: "Iphone 8",
description:
"The iPhone 8 ",
price: 19900,
discontinued: false,
categories: ["c1"]
},
{
id: "i10",
title: "Iphone X",
description: "Iphone 10",
price: 39900,
discontinued: false,
categories: ["c1"]
},
{
id: "i11",
title: "Iphone 11",
description: "The iPhone 11 ",
price: 69900,
discontinued: false,
categories: ["c1"]
};
my function
function myFunction() {
document.getElementById("selected-category").innerHTML = "Iphones";
document.getElementById("category-products").innerHTML = "";
for (i = 0; i < 20; i++) {
document.getElementById("category-products").innerHTML = window.id;
const found = window.products.find(window.products.categories == "c1");
console.log(found);
}
}
part html code with button
<button onclick="myFunction()">Iphones</button>
First, you have syntax error in your windows.products = [ ... };. It's not closed properly (you need a ] before the };.
Then, the find method needs to be passed a function that processes an element of the Array. What you tried window.products.categories == "c1" evaluates to false, because the property categories does not exist in the window.products array. You get undefined on the left hand side and a string on the right hand side, so it's always false. You'd get "false is not a function".
Examples of using find() with a function:
const found = window.products.find( element => element.categories == "c1" );
or
const found = window.products.find( function(element) {
return element.categories == "c1"
});
But then:
The above == "c1" isn't what you should use, because it only matches due to type coercion from an array to a string, and only matches when the categories has only "c1" and no other elements. You should use includes method.
"find" will only give you one matching product. Use "filter" to find all matching ones.
If you only need to search for one key "c1", you can use
const found = window.products.filter( product=> product.categories.includes("c1")); and for two keys "c1" and "c2": const found = window.products.filter( product => product.categories.includes("c1") && product.categories.includes("c2"));
But I don't think you should use the above, because you should handle the case where the user searches for multiple keys.
const searchList = ['c1', 'c2', 'c3']
const found = window.products.filter ( function( product) {
//returns true if element.categories contains all of 'c1', 'c2' and 'c3'
return searchList.every( searchKey => product.categories.includes(searchKey)) ;
})
You can also do the search in one line, but may be harder to read :
const searchList = ['c1', 'c2', 'c3']
const found = window.products.filter ( product => searchList.every( searchKey => product.categories.includes(searchKey)) );
I am trying to create a pretty Objects of arrays but this come instead.
{
"label": [
"Instagram"
],
"value": [
"#username"
]
}
So, how can I change it? What I want is to be like this
{
"label": "instagram",
"value": "#username"
},
I don't know how it happened, but my guess is it was the result of me using formik to define initialValues of a complex nested array from strapi. I was using array.map to map the objects. Hence perhaps thats why it was so messy.
So what is the solution for this? Formatting Arrays of Arrays into Objects? Merging? Converting? I have no idea what it was called. Thanks in advance for anybody replying this.
(updated) The initialValues:
const formik = useFormik({
enableReinitialize: true,
initialValues: {
name: vendor?.name || '',
description: vendor?.description || '',
company: {
social_media: [
{
label: vendor.company?.social_media.map((x) => x.label) || '',
value: vendor.company?.social_media.map((x) => x.value) || ''
}
]
}
},
You can use for..in and array join method
const data = {
"label": [
"Instagram"
],
"value": [
"#username"
]
};
for (let keys in data) {
data[keys] = data[keys].join(',')
};
console.log(data)
Array.map returns an array if you don't want any object use Array.foreach
Or
Use [...Array.map()]
label: [...vendor.company?.social_media.map((x) => x.label)]
value: [...vendor.company?.social_media.map((x) => x.value)
I have an array of objects as part of a data response that I am grouping together using lodash's groupBy via each object's groupName key.
Some of the items that come back have a groupName value of null, undefined or an empty string and lodash creates separate groups for each of those values.
I combine all of the falsey groups into a single group name "Uncategorized" and attempt to remove the original falsey groups to only return "Uncategorized" and all other truthy groups.
The problem I'm running into is that I'm trying to use the rest operator to remove the original falsy objects with undefined, null, and empty string keys by assigning them to a variable like let groupKeysToRemove = ['undefined', 'null', ''] and then trying to remove them like let { [groupKeysToRemove]: removed, ...groups } = initialGroups; but it returns the same Object with nothing removed. I'm not sure if my syntax is wrong or what but I am stumped.
Code via sandbox:
const resources = [
{
groupName: undefined,
name: "color organizer"
},
{
groupName: null,
name: "Bart_Simpson_200px"
},
{
groupName: "Spread Sheets",
name: "Backflow"
},
{
groupName: "Spread Sheets",
name: "220px-Marge_Simpson"
},
{
groupName: "",
name: "212px-Homer_Simpson_2006"
},
{
groupName: "Spread Sheets",
name: "Product 6"
},
{
groupName: "Warranties",
name: "Warranty Bart Simpson"
},
{
groupName: "Warranties",
name: "Warranty Product 2"
},
{
groupName: "Warranties",
name: "Warranty Product 3"
}
];
let initialGroups = groupBy(resources, "groupName");
let uncategorizedGroups = [];
uncategorizedGroups.push(...initialGroups[undefined], ...initialGroups[null], ...initialGroups[""]);
const renameGroups = uncategorizedGroups.map((object) => {
object.groupName = "Uncategorized";
return object;
});
const renamedGroups = groupBy(renameGroups, "groupName");
console.log('RENAMED GROUPS: ', renamedGroups)
const groupKeysToRemove = "undefined"
let { [groupKeysToRemove]: removed, ...groups } = initialGroups;
groups = { ...groups, ...renamedGroups };
Think of the brackets syntax [] for the destructing operation as an index to a property of an object, not an array that you pass in. It's analogous to calling for example obj["a"] vs obj.a to access the a field on obj.
So knowing this, you need to pass in 3 arguments to extract the values that you want to remove. For null and undefined I had to put them in separate variables, it wasn't working when putting them directly in the brackets:
const nullKey = null;
const undefinedKey = undefined;
let {
[nullKey]: nullGroup,
[undefinedKey]: undefinedGroup,
[""]: emptyStringGroup,
...groups } = initialGroups;
groups = { ...groups, ...renamedGroups };
console.log("initialGroups: ", initialGroups);
console.log("GROUPS: ", groups);
console.log("null group", nullGroup)
console.log("undef group", undefinedGroup)
console.log("emptyStringGroup group", emptyStringGroup)
I would like to get an array of the Main by category
[
{
"#id": "/api/main/7648",
"#type": "Main",
category: [{
name: "laboriosam"
}]
},
{
"#id": "/api/main/7647",
"#type": "Main",
category: [{
name: "foo"
},
{
name: "bar"
}
]
}
]
So I try:
console.log([...this.state.mains].filter(main => main.category.filter(category => category.name == "foo")))
But it returns me everything and I don't understand why?
Using Array.prototype.some, you can decide if the value is existed or not.
Using Array.filter function, it filters the true values only so it is needed to return boolean value on callback.
const input = [{
"#id": "/api/main/7648",
"#type": "Main",
category: [{
name: "laboriosam"
}]
},
{
"#id": "/api/main/7647",
"#type": "Main",
category: [{
name: "foo"
},
{
name: "bar"
}
]
}
];
console.log(input.filter(main => main.category.some(category => category.name == "foo")))
I find your question really hard to understand. If you want just the objects with #type = "Main" you can do
this.state.mains.filter(x => x["#type"] === "Main");
If you then want to filter those out who don't have a certain category you can add another filter like so:
this.state.mains
.filter(x => x["#type"] === "Main")
.filter(x => x.category.findIndex(c => c.name === "foo") !== -1);
Obviously in case of big array you also put both checks into one filter, but that might not be as readable.
Also note that [...this.state.mains].filter(...) is redundant, as .filter() already returns a new array.
I'm studying about JSON and its use cases. Suppose I have a recipe book and I have the following JSON to store recipes (I apologize if anything is wrong before hand, I'm just starting with this)
var recipeBook =
{
recipes:
[
{
name: 'Spaghetti',
ingredients:
[
{
ingredientName: 'Pasta',
requiredAmount: 1,
},
{
ingredientName: 'Tomato Sauce',
requiredAmount: 1,
}
]
},
{
name: 'Cereal',
ingredients:
[
{
ingredientName = 'Cereal Box',
requiredAmount = 1
},
{
ingredientName = 'Milk',
requiredAmount = '1'
}
]
}
]
}
Say I wanted to add a third recipe, or add a new ingredient to a recipe...I'm wondering what is the best option (code-wise) to add new data into this JSON.
I think propably the function you are looking for is https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/push, for example
recipeBook.recipes.push({
name: 'Soup',
ingredients:
[
{
ingredientNam: 'Water',
requiredAmount: 1
},
{
ingredientName: 'Potatoes',
requiredAmount: 4
}
]
})
And for new ingredients it would be
recipeBook.recipes[0].ingredients.push(
{
ingredientName: 'Salt',
requiredAmount: 1
}
)
I would recomend you to use the name of the recipe as the key in the recipes, I mean:
var recipeBook =
{
recipes:
{
Spaghetti:
{
ingredients:
[
{
ingredientName: 'Pasta',
requiredAmount: 1,
},
{
ingredientName: 'Tomato Sauce',
requiredAmount: 1,
}
]
},
Cereal:
{
ingredients:
[
{
ingredientName: 'Cereal Box',
requiredAmount: 1
},
{
ingredientName: 'Milk',
requiredAmount: 1
}
]
}
}
}
This helps to get the recipe as
var spaghettiRecipe = recipeBook.recipes.Spaghetti
First, JSON vs. JS Objects
JSON isn't to be confused with generic objects in JavaScript. JSON is a "lightweight data-interchange format" that does its best to be both easy for humans to read and edit, yet also easy for computers to parse and generate. One of the main differences is that JSON has stricter syntax requirements than generic JS objects do.
What you've written is simply an object declaration in Javascript, rather than standard JSON. A JSON equivalent to your example data would look like this:
{
"recipes": [
{
"name": "Spaghetti",
"ingredients": [
{
"ingredientName": "Pasta",
"requiredAmount": 1
},
{
"ingredientName": "Tomato Sauce",
"requiredAmount": 1
}
]
},
{
"name": "Cereal",
"ingredients": [
{
"ingredientName": "Cereal Box",
"requiredAmount": 1
},
{
"ingredientName": "Milk",
"requiredAmount": 1
}
]
}
]
}
The specific distinctions here:
All property names are enclosed in double quotes "
All strings are enclosed in double quotes " rather than single '
Trailing commas that aren't followed by another key/ value pair or data structure aren't allowed
You could choose to omit the recipes property and keep just the array without the enclosing object, this would still be valid.
Also worth noting that prop = value is not allowed in either syntax, JS object declaration nor JSON.
A description of the full JSON spec is here if you're interested.
Manipulating JS Data
You asked what the best option code-wise is to add data to this JSON. Since JSON is just a format, it's not actually super relevant here, since it's primarily used for data storage and transport, and not for manipulation.
To work with data stored as JSON in Javascript, you can parse it in with the JSON.parse(string) method, which will read a JSON string and return an equivalent Javascipt object. You can then use standard object/ array manipulation methods and techniques on that resulting object, in this case using push() to add a new recipe onto the array.
Here is some sample code to add data to the object
//add recipe
recipeBook.recipes.push(newRecipe);
//add ingredient to recipe by name
var recipe = recipeBook.recipes.find(r => r.name == recipeName);
if (recipe) recipe.ingredients.add(newIngredient);
Also your Cereal object is using ingredientName = 'Cereal Box', which is invalid javascript syntax. You need to use : as in your spaghetti object.
Instantiating an object involves creating an object programmatically. I'm simplifying the code to make it easy:
var book = {}
function Recipe(name, ingredients) {
this.name = name;
this.ingredients = ingredients;
}
// instantiate it
var chicken_teriyaki = new Recipe('Teriyaki', ['new one', 'two', 'three'])
var fried_chicken = new Recipe('Fried Chicken', ['chicken', 'potato starch', 'oil'])
// add to book
book.chicken_teriyaki = chicken_teriyaki;
book.fried_chicken = fried_chicken;
// update recipe
book.chicken_teriyaki.ingredients.push('another yummy')
// view result
console.log('the book', book);