Generating Entries with Same Name in JavaScript Object - javascript

I'm new to Javascript and I've done a little research but I can't seem to figure out how to generate multiple lists with same name keys but different values. I'm trying to generate code for an embed message which should look something like this:
{embed: {
color: 3447003,
title: "title",
description: "desc",
fields: [{
name: "header 1",
value: "text 1"
},
{
name: "header 2",
value: "text 2"
},
{
name: "header 3",
value: "text 3"
}
]
}
}
this is for generating a list of my commands in an embed automatically so I don't have to keep going back and edit it.
I'm mainly trying to get multiple of the "fields" with the "name" and "value" entries and also trying to add all the commands in a line for the "value".
Here's my code:
let currentCategory = "";
var embed = {
"title": "= __Command List__ =",
"description": `[Use ${message.settings.prefix}help <commandname> for details]`,
"color": 2563607,
fields : []
};
const sorted = myCommands.array().sort((p, c) => p.help.category > c.help.category ? 1 : p.help.name > c.help.name && p.help.category === c.help.category ? 1 : -1 );
sorted.forEach( c => {
const cat = c.help.category.toProperCase();
if (currentCategory !== cat) {
embed.fields = [{name : `${cat}`,value : ""}];
currentCategory = cat;
}
embed.fields[0].value += ` \`${c.help.name}\``;
});
console.log({embed});
message.channel.send({embed});
I used console.log({embed}); to print the code it generates in the console and this is what shows.
{ embed:
{ title: '= __Command List__ =',
description: '[Use y!help <commandname> for details]',
color: 2563607,
fields: [ [Object] ] } }

Ok I figured it out thanks to PM 77-1.
For anyone else who wants to know I basically set and index of -1 and made it add to the index while it looped for every new category.
let currentCategory = "";
let index = -1;
var embed = {
"title": "= __Command List__ =",
"description": `[Use ${message.settings.prefix}help <commandname> for details]`,
"color": 2563607,
fields : []
};
const sorted = myCommands.array().sort((p, c) => p.help.category > c.help.category ? 1 : p.help.name > c.help.name && p.help.category === c.help.category ? 1 : -1 );
sorted.forEach( c => {
const cat = c.help.category.toProperCase();
if (currentCategory !== cat) {
index = index + 1
embed.fields[index] = {name : `${cat}`,value : ""};
currentCategory = cat;
}
embed.fields[index].value += ` \`${c.help.name}\``;
});
console.log({embed});
message.channel.send({embed});

Related

How to create a sub-associative object from a list of array keys in JavaScript?

I have an object from user input. The keys to that object are separated by commas, and I just want to separate those keys and make the keys of the object.
The key_array below is dynamic from user input, generates a different array each time, below I give you an example.
I have shown the object in my code which you can see below. you can also see the output by running that code.
var main_array = {};
var key_array = {
'user,name' : 'user name',
'user,email' : 'Email address',
'order,id' : 123456,
'order,qty' : 2,
'order,total' : 300,
'order,product,0,name' : "product1",
'order,product,0,qty' : 1,
'order,product,0,price' : 100,
'order,product,1,name' : "product2",
'order,product,1,qty' : 1,
'order,product,1,price' : 200,
};
for (keys in key_array){
var value = key_array[keys];
// What do I do here to get the output I want?
main_array['[' + keys.split(",").join('][')+ ']'] = value;
}
console.log(main_array);
Running the code above will give you the following output which is incorrect. And the output I don't want.
{
[order][id]: 123456,
[order][product][0][name]: "product1",
[order][product][0][price]: 100,
[order][product][0][qty]: 1,
[order][product][1][name]: "product2",
[order][product][1][price]: 200,
[order][product][1][qty]: 1,
[order][qty]: 2,
[order][total]: 300,
[user][email]: "Email address",
[user][name]: "user name"
}
I want an output like JSON below, so please tell me how to do it.
{
"user":{
"email" : "Email address",
"name" : "user name"
},
"order":{
"id" : 123456,
"qty" : 2,
"total" : 300,
"product":[
{
"name" : "product1",
"price" : 100,
"qty" : 1
},{
"name" : "product2",
"price" : 200,
"qty" : 1
}
]
}
}
Note: Please do not use eval, as using eval in this way is terribly unreliable, bad work and unsafe. Because I get all my data from user input, the likelihood of abuse can increase.
Use Object.entries to go over key and values of object.
Split the key by , separator and then build the object.
While building object, make sure to merge the keys and values using mergeTo method.
Then convert the objects which has the numerical keys then convert to object using convertObjsToArray method.
var key_array = {
"user,name": "user name",
"user,email": "Email address",
"order,id": 123456,
"order,qty": 2,
"order,total": 300,
"order,product,0,name": "product1",
"order,product,0,qty": 1,
"order,product,0,price": 100,
"order,product,1,name": "product2",
"order,product,1,qty": 1,
"order,product,1,price": 200
};
const mergeTo = (target, obj) => {
Object.entries(obj).forEach(([key, value]) => {
if (typeof value === "object" && !Array.isArray(value)) {
if (!target[key]) {
target[key] = {};
}
mergeTo(target[key], obj[key]);
} else {
target[key] = value;
}
});
};
const convertObjsToArray = obj => {
Object.entries(obj).forEach(([key, value]) => {
if (typeof value === "object") {
if (Object.keys(value).every(num => Number.isInteger(Number(num)))) {
obj[key] = Object.values(value);
} else {
convertObjsToArray(obj[key]);
}
}
});
};
const res = {};
Object.entries(key_array).map(([key, value]) => {
const keys = key.split(",");
let curr = { [keys.pop()]: value };
while (keys.length > 0) {
curr = { [keys.pop()]: curr };
}
mergeTo(res, curr);
});
convertObjsToArray(res);
console.log(res);
You can create the objects and keys required from the string dynamically, take each key and split it to an array using split(','). Using each item in the array create the structure required. Assuming if a key is a number, then it's parent must be an array.
Object.keys(key_array).forEach(key => {
const path = key.split(',');
let current = main_array;
for (let i = 0; i < path.length - 1; i++) {
if (!current[path[i]]) {
current[path[i]] = path[i + 1] && !isNaN(path[i + 1]) ? [] : {};
}
current = current[path[i]];
}
current[path.pop()] = key_array[key];
});
console.log(main_array); // Desired result

How to add <hr> after every new category

I have a array of object called elements, and the objects have two values (name and category).
[
{name : 'key1', category : 'tech'},
{name : 'key2', category : 'tech'},
{name : 'key3', category : 'tech'},
{name : 'cable1' , category : 'hard'}
{name : 'cable2' , category : 'hard'}
{name : 'cable3' , category : 'hard'}
{name : 'cable4' , category : 'hard'}
]
I want to display all names but add an <hr> whenever reaches a new category
Please help and thank you of helping.
I would first group your objects by category using Array.prototype.reduce(), then iterate over each category using Array.prototype.map():
const data = [
{name : 'key1', category : 'tech'},
{name : 'wire1' , category : 'misc'},
{name : 'key2', category : 'tech'},
{name : 'cable1' , category : 'hard'},
{name : 'key3', category : 'tech'},
{name : 'cable2' , category : 'hard'},
{name : 'wire2' , category : 'misc'}
];
const dataMap = data.reduce((acc, x) => {
acc[x.category] = [...(acc[x.category] || []), x];
return acc;
}, {});
const html = Object.entries(dataMap).map(([cat, items]) => {
return items.map(item => `<div>${item.name} ${item.category}</div>`).join('');
}).join('<hr>');
document.getElementById('app').innerHTML = html;
<div id="app"></div>
You can try something like this,
var category;
$.each(object,function(i,objval)
{
console.log(objval['name']);
if(category != "" && category != objval['category'])
{
console.log("<hr>");
}
category = objval['category'];
});
How about something like:
prev_category = undefined;
elements.forEach(function(e) {
if (i > 0 && e.category != prev_category) {
console.log('<hr>');
}
prev_category = e.category;
console.log(e.name);
});
(of course, you can replace the console.log() commands with whatever you really want to do with those texts, e.g. append them to one big string)
Iterate the object and use template literals to create the dom and check if the index of the array is not same as length then add an hr
let elements = [{
name: 'key',
category: 'tech'
},
{
name: 'cable',
category: 'hard'
}
]
let str = '';
elements.forEach(function(item, index) {
str += `<div class='elem'><span>${item.name}</span><span> ${item.category}</span></div>`
if (index !== elements.length - 1) {
str += `<hr>`
}
});
document.getElementById('container').innerHTML = str
<div id='container'></div>
If you are looking for just border then use css pseudo selector
let elements = [{
name: 'key',
category: 'tech'
},
{
name: 'cable',
category: 'hard'
}
]
let str = '';
elements.forEach(function(item, index) {
str += `<div class='elem'><span>${item.name}</span><span> ${item.category}</span></div>`
});
document.getElementById('container').innerHTML = str
.elem:not(:last-child) {
border-bottom: 1px solid black;
}
<div id='container'></div>
Basically you need to sort the data by category first then, render the element, I use react code as example
const data = [
{
name: "Huawei",
category: "phone"
},
{
name: "Iphone",
category: "phone"
},
{
name: "Refacoring Improving the design of existing code",
category: "book"
},
{
name: "Python Crash Course",
category: "book"
},
{
name: "My heart will go on",
category: "music"
},
{
name: "I'm your angel",
category: "music"
}
];
function generateCom(data) {
let listComps = [];
let category = "";
// sort the data by category
data.sort((a, b) => (a.category > b.category ? 1 : -1));
// generate the component by category
data.forEach((ele, idx) => {
if (idx === 0) {
listComps.push(<h3>{ele.category}</h3>);
listComps.push(<li>{ele.name}</li>);
category = ele.category;
return;
}
if (ele.category === category) {
listComps.push(<li>{ele.name}</li>);
} else {
listComps.push(<hr />);
listComps.push(<h3>{ele.category}</h3>);
listComps.push(<li>{ele.name} </li>);
category = ele.category;
}
});
return listComps;
}
can refer to the example
https://codesandbox.io/embed/6x0p7908qw

Retrieve an object from a two dimensional array matching a specific parameter [duplicate]

This question already has answers here:
Find object by id in an array of JavaScript objects
(36 answers)
Closed 5 years ago.
So I have a 2d array containing multiple objects. Each object has multiple properties and methods. I would like to return only the objects methods and properties that have a matching id to what I pass it. in this case, the id is 1.
const blogData = [
{
title : "Title 1",
date : "2017-07-15",
id : 1
},
{
title : "Title 2",
data : "2017-07-16",
id : 2
}
];
for (let i = 0; i < blogData.length; i++) {
if (blogData[i].id === 1) {
console.log(`Post #${blogData[i].id} loaded`);
}
}
You can filter the array based on ID, and assuming you just have one hit, you can return the first (and only) item, or skip shift() and get an array of matches.
const blogData = [{
title: "Title 1",
date: "2017-07-15",
id: 1
},
{
title: "Title 2",
data: "2017-07-16",
id: 2
}
];
var result = blogData.filter( x => x.id === 1).shift();
console.log(result)
You can use a generic function that will work with any ID and any list.Something like:
const blogData = [
{
title : "Title 1",
date : "2017-07-15",
id : 1
},
{
title : "Title 2",
data : "2017-07-16",
id : 2
}
];
function getData(id, arr, callback){
$.each(arr, function(key, value){
if(value.id === id)
callback(value); //Just using a simple callback for console purposes
});
}
$(document).ready(function(){
getData(1, blogData, function(c){
console.log(c); //loggin the callback
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
In your case this is an array of objects and not a 2 dimentional array, so you can use Array.prototype.filter() method to filter only the object that has the id=1:
var res = blogData.filter(function(obj){
return obj.id === searchedId;
}).shift();
Demo:
const blogData = [
{
title : "Title 1",
date : "2017-07-15",
id : 1
},
{
title : "Title 2",
data : "2017-07-16",
id : 2
}
];
var searchedId = 1;
var res = blogData.filter(function(obj){
return obj.id === searchedId;
}).shift();
console.log(res);
And using your method with foor loop, you just need to return the right object if the condition is matched:
var result = {};
for (let i = 0; i < blogData.length; i++) {
if (blogData[i].id === 1) {
result = blogData[i]
}
}

push existing array into multiple arrays js

I want to create an array for active items and inactive items in my list. I currently have the below code. The below code works however I want the format to be the same as the existing array.
var myData = [
{"text" : "item 1", "id":11111, "active" : 0 },
{"text" : "item 2", "id":22222, "active" : 1 },
{"text" : "item 3", "id":33333, "active" : 1 },
{"text" : "item 4", "id":44444, "active" : 0 }
];
console.log(myData[0].text); //returns item 1
var active = [];
var inactive = [];
for (var i = 0; i < myData.length; i++) {
if(myData[i].active) {
active.push({
items: myData[i];
});
}
else {
inactive.push({
items: myData[i];
});
}
}
console.log(active[0].items.text); //returns item 2
console.log(inactive[0].items.text); //returns item 1
I can't seem to work out how to push the whole object into the array without naming it.
I want to setup my array so that I can console.log
active[0].text
rather than having to go to the next level and go
active[0].items.text
Is there a way I can push the whole object without naming it?
var myData = [
{"text" : "item 1", "id":11111, "active" : 0 },
{"text" : "item 2", "id":22222, "active" : 1 },
{"text" : "item 3", "id":33333, "active" : 1 },
{"text" : "item 4", "id":44444, "active" : 0 }
];
console.log(myData[0].text); //returns item 1
var active = myData.filter(function(data){
return data.active;
});
var inactive = myData.filter(function(data){
return !data.active;
});
Or perhaps make it a function
function getData(type){
return myData.filter(function(data){
return (type == 'active') ? data.active : !data.active;
});
}
and if you're already using ES6 arrow functions you can shorten them to:
var active = myData.filter(data => data.active);
var inactive = myData.filter(data => !data.active);
function getData(type){
return myData.filter(data => (type == 'active') ? data.active : !data.active);
}
var myData = [
{"text" : "item 1", "id":11111, "active" : 0 },
{"text" : "item 2", "id":22222, "active" : 1 },
{"text" : "item 3", "id":33333, "active" : 1 },
{"text" : "item 4", "id":44444, "active" : 0 }
];
var active = [];
var inactive = [];
for (var i in myData){
var item = myData[i];
if (item.active){
active.push(item);
}else{
inactive.push(item);
}
}
console.log(active, inactive);
If I'm understand you correctly, all you need to do is active.push(myData[i]) to push the reference into the array and do the same for inactive.
Instead of pushing a new object containing the active/inactive objects, just push the existing object itself.
var active = [];
var inactive = [];
for (var i = 0; i < myData.length; i++) {
if(myData[i].active) {
active.push(myData[i]);
} else {
inactive.push(myData[i]);
}
}

JavaScript SUM and GROUP BY of JSON data

This is my first attempt at doing JavaScript with some JSON data objects and need some advice on the proper way to attain my goal.
Some server-side code actually generates a JSON formatted string that I have to work with and assign it to a string:
var dataString='$DATASTRING$';
But the end-result I have to work with after the server substitutes its data (without the \r\n, of course):
var dataString='[
{ "category" : "Search Engines", "hits" : 5, "bytes" : 50189 },
{ "category" : "Content Server", "hits" : 1, "bytes" : 17308 },
{ "category" : "Content Server", "hits" : 1, "bytes" : 47412 },
{ "category" : "Search Engines", "hits" : 1, "bytes" : 7601 },
{ "category" : "Business", "hits" : 1, "bytes" : 2847 },
{ "category" : "Content Server", "hits" : 1, "bytes" : 24210 },
{ "category" : "Internet Services", "hits" : 1, "bytes" : 3690 },
{ "category" : "Search Engines", "hits" : 6, "bytes" : 613036 },
{ "category" : "Search Engines", "hits" : 1, "bytes" : 2858 }
]';
And then I can change it to an object to work with.
var dataObject=eval("("+dataString+")");
This allows me to access the data individual rows of data, but I need to sum, group by, and order the values.
I need to the equivalent of an SQL statement like this:
SELECT category, sum(hits), sum(bytes)
FROM dataObject
GROUP BY category
ORDER BY sum(bytes) DESC
My desired output would be an object like this that I can further process:
var aggregatedObject='[
{ "category" : "Search Engines", "hits" : 13, "bytes" : 673684 },
{ "category" : "Content Server", "hits" : 3, "bytes" : 88930 },
{ "category" : "Internet Services", "hits" : 1, "bytes" : 3690 },
{ "category" : "Business", "hits" : 1, "bytes" : 2847 }
]';
...but i don't know where to start.
I could loop through all the category values and find the unique categories first, then loop again and sum the hits and bytes, then again to sort, but it seems there has got to be an easier way.
prototype.js (1.7) is already included on the client page, but I could add Underscore, jQuery, or some other small library if I had to.
I just don't know what would be best, easiest, smallest with the least amount of code to process the query.
Any suggestions?
You can use the native functions .reduce() to aggregrate the data, and then .sort() to sort by bytes.
var result = dataObject.reduce(function(res, obj) {
if (!(obj.category in res))
res.__array.push(res[obj.category] = obj);
else {
res[obj.category].hits += obj.hits;
res[obj.category].bytes += obj.bytes;
}
return res;
}, {__array:[]}).__array
.sort(function(a,b) { return b.bytes - a.bytes; });
If you're supporting older implementations, you'll need to use a shim for .reduce().
If you go the LINQ.js route, you can do it like this:
var aggregatedObject = Enumerable.From(dataArray)
.GroupBy("$.category", null,
function (key, g) {
return {
category: key,
hits: g.Sum("$.hits"),
bytes: g.Sum("$.bytes")
}
})
.ToArray();
Working demo with Stack Snippets:
var dataArray = [
{ category: "Search Engines", hits: 5, bytes: 50189 },
{ category: "Content Server", hits: 1, bytes: 17308 },
{ category: "Content Server", hits: 1, bytes: 47412 },
{ category: "Search Engines", hits: 1, bytes: 7601 },
{ category: "Business", hits: 1, bytes: 2847 },
{ category: "Content Server", hits: 1, bytes: 24210 },
{ category: "Internet ", hits: 1, bytes: 3690 },
{ category: "Search Engines", hits: 6, bytes: 613036 },
{ category: "Search Engines", hits: 1, bytes: 2858 }
];
var aggregatedObject = Enumerable.From(dataArray)
.GroupBy("$.category", null,
function (key, g) {
return {
category: key,
hits: g.Sum("$.hits"),
bytes: g.Sum("$.bytes")
}
})
.ToArray();
console.log(aggregatedObject);
<script src="//cdnjs.cloudflare.com/ajax/libs/linq.js/2.2.0.2/linq.min.js"></script>
Also, you can find more info on linqjs group by with a sum
Given the dataString above, the below code seems to work. It goes through each object; if the category exists in the groupedObjects array, its hits and bytes are added to the existing object. Otherwise, it is considered new and added to the groupedObjects array.
This solution makes use of underscore.js and jQuery
Here's a jsfiddle demo: http://jsfiddle.net/R3p4c/2/
var objects = $.parseJSON(dataString);
var categories = new Array();
var groupedObjects = new Array();
var i = 0;
_.each(objects,function(obj){
var existingObj;
if($.inArray(obj.category,categories) >= 0) {
existingObj = _.find(objects,function(o){return o.category === obj.category; });
existingObj.hits += obj.hits;
existingObj.bytes += obj.bytes;
} else {
groupedObjects[i] = obj;
categories[i] = obj.category;
i++;
}
});
groupedObjects = _.sortBy(groupedObjects,function(obj){ return obj.bytes; }).reverse();
var obj = [{Poz:'F1',Cap:10},{Poz:'F1',Cap:5},{Poz:'F1',Cap:5},{Poz:'F2',Cap:20},{Poz:'F1',Cap:5},{Poz:'F1',Cap:15},{Poz:'F2',Cap:5},{Poz:'F3',Cap:5},{Poz:'F4',Cap:5},{Poz:'F1',Cap:5}];
Array.prototype.sumUnic = function(name, sumName){
var returnArr = [];
var obj = this;
for(var x = 0; x<obj.length; x++){
if((function(source){
if(returnArr.length == 0){
return true;
}else{
for(var y = 0; y<returnArr.length; y++){
var isThere = [];
if(returnArr[y][name] == source[name]){
returnArr[y][sumName] = parseInt(returnArr[y][sumName]) + parseInt(source[sumName]);
return false;
}else{
isThere.push(source);
}
}
if(isThere.length>0)returnArr.push(source);
return false;
}
})(obj[x])){
returnArr.push(obj[x]);
}
}
return returnArr;
}
obj.sumUnic('Poz','Cap');
// return "[{"Poz":"F1","Cap":45},{"Poz":"F2","Cap":25},{"Poz":"F3","Cap":5},{"Poz":"F4","Cap":5}]"
Hi here is one solution written by me Visit: aggregate_groupby_js on npm
or in aggregate_groupby_js on github
The javascript library for using aggregate functions on array of objects. Basic functions like SUM, MIN, MAX, AVG, DISTINCT_COUNT for entire javascript objects
Example:
var arr = [{`"shape"`:`"square"`,`"color"`:`"red"`,`"used"`:1,`"instances"`:1},
{`"shape"`:`"square"`,`"color"`:`"red"`,`"used"`:2,`"instances"`:1},
{`"shape"`:`"circle"`,`"color"`:`"blue"`,`"used"`:0,`"instances"`:0},
{`"shape"`:`"square"`,`"color"`:`"blue"`,`"used"`:4,`"instances"`:4},
{`"shape"`:`"circle"`,`"color"`:`"red"`,"`used"`:1,`"instances"`:1},
{`"shape"`:`"circle"`,`"color"`:`"red"`,`"used"`:1,`"instances"`:0},
{`"shape"`:`"square"`,`"color"`:`"blue"`,`"used"`:4,`"instances"`:5},
{`"shape"`:`"square"`,`"color"`:`"red"`,`"used"`:2,`"instances"`:1}];
// Specify columns
var columns =[`"used"`, `"instances"`];
// Initialize object
var gb = new GroupBy(arr,columns);
// or
var gb = new GroupBy(arr,[`"used"`, `"instances"`]);
// Call the aggregate functions
gb.sum();
gb.min();
gb.max();
gb.avg();
gb.distinctCount();

Categories