Merge JSON arrays - javascript

I have an array (json) of products (around 5000) where each product is represented in two arrays as shown below;
["PC01","Product 1","11"],
["PC01","Product 1","17"]
The only difference is, the 11 and 17 strings both representing the first and second price of the product.
I am trying to find a way to merge this array into a single one so that each product is represented as one such as:
["PC01", "Product 1", "11", "17"]
I have tried looping and forming an object of products which I found not quite safe due to various reasons and one of them being the order of the prices are unknown. So at first I might get the highest price first and lowest price second while on the other hand, another array would have the lowest one first and highest the second.. This is my current code;
var products = [];
$.getJSON('data.json', function (data) {
$.each(data, function (key, val) {
if (!key == 0) {
// because I use the previous arrays info,
// only start if we HAVE a previous array hence;
if (data[key][0] != data[key - 1][0]) {
var o = {}
o.ProductCode = val[0];
o.ProductName = val[1];
o.Price1 = val[3];
o.Price2 = data[key - 1][3];
products.push(o);
}
}
});
});
As I said, this relies on the fact that each product exists as two arrays and assumes the second price is the highest.
So, I am basically trying to find the most optimal way to parse the json to merge all double arrays and would be grateful if you could give me some ideas..
EDIT:
Sample Data:
Note: Sample data have more information than I initially wrote in my question such as stock quantity, VAT rate etc. but omitted by me to prevent any confusions.
[
[
"AD.0001 ",
"AD ALOE VERA MOUTHWASH w�th FLUORIDE 250ML ",
" 691",
" 11.51",
" 16",
" 96"
],
[
"AD.0001 ",
"AD ALOE VERA MOUTHWASH w�th FLUORIDE 250ML ",
" 691",
" 17",
" 16",
" 96"
],
[
"AD.0002 ",
"AD ALOE VERA MOUTHWASH 250ML ",
" 692",
" 11.51",
" 16",
" 75"
],
[
"AD.0002 ",
"AD ALOE VERA MOUTHWASH 250ML ",
" 692",
" 17",
" 16",
" 75"
]
]

Try something like this:
var productsbycode = {};
var products = [];
$.getJSON('data.json', function (data) {
$.each(data, function (key, val) {
if(typeof productsbycode[val[0]] !== "object"){
productsbycode[val[0]] = {name: val[1], prices: [val[2]]};
} else{
productsbycode[val[0]].prices.push(val[2]);
}
});
$.each(productsbycode, function(key, val){
var price1 = val['prices'][0];
var price2 = val['prices'][1];
if(price1 < price2){
products.push([key, val['name'], price1, price2]);
} else{
products.push([key, val['name'], price2, price1]);
}
});
});
JSFiddle.
It works in any order and it does what you asked.
Update: The prices will now show the lowest first and then the highest.

If the 1 and 2 always follow each other just do this:
var products = [
["A", 1], ["A", 2],
["B", 5], ["B", 2],
["C", 1], ["C", 2]
];
document.getElementById('input').innerHTML = JSON.stringify(products);
var i = 0;
while(i < products.length) {
var first = i,
second = i + 1;
if ( products[first][1] > products[second][1] ) {
first = i + 1; second = i; // just switch the index..
}
products[first].push(products[second][1]);
products.splice(second, 1);
i++;
}
document.getElementById('result').innerHTML = JSON.stringify(products);
input
<pre id="input"></pre>
<br>
result
<pre id="result"></pre>

More broadly, there's a cool library called Immutable.js which lets you manipulate complex JSON objects in a very clean way.
It provides methods to merge arrays and other arbitrary JSON objects and define your conflict resolution strategy.

There is Linq for JavaScript (linq.js), a free JavaScript library available at CodePlex:
https://linqjs.codeplex.com/
Like C# LINQ it has a .distinct method which can help you to remove duplicates, amongst most LINQ query methods you know from C# made available in JavaScript.
Example:
var list = [
{ one: 2, two: 4, three: 1 },
{ one: 4, two: 7, three: 5 },
{ one: 2, two: 4, three: 1 },
{ one: 7, two: 3, three: 2 },
];
Enumerable.From(list).Distinct("$.one")
.OrderBy("$.a").ThenBy("$.two").ThenBy("$.three")
.Select("$.one + ':' + $.two + ':' + $.three")
Gives you the result:
2:4:1
4:7:5
7:3:2
More about LINQ.js: see this answer at SO

Related

JavaScript user prompt 2d array lookup - Elf Name Generator

Started with what I thought was a simple idea for a class activity for our JavaScript unit, but falling foul best way to read a JavaScript 2D array based on a user input.
How can I get it to look up the users entry in the first index of the array and output the value in the second index? I need to tell it at some point which index to read and which to output, via similar syntax to [0][1] I imagine, but trial and error is getting me far.
Perhaps there is a better way??
Here is what I have so far, which just outputs the first array entry in its entirety - "A","Angelic"
var firstNames = [
["A","Angelic"],
["B","Blustery"],
........
["Y","Cheery"],
["Z","Dancy"]
];
var firstInitial = window.prompt("What is the first letter of your first name: ").toUpperCase();
let elfFirstName = firstNames.find(firstSearch);
function firstSearch(firstInitial) {
return firstInitial;
}
window.alert("Your Elf Name is " + elfFirstName);
</script>```
Thanks
You could take an object with the initials as keys.
const
firstNames = {
A: "Angelic",
B: "Blustery",
Y: "Cheery",
Z: "Dancy"
},
firstInitial = window.prompt("What is the first letter of your first name: ")[0].toUpperCase(),
elfFirstName = firstNames[firstInitial];
console.log("Your Elf Name is " + elfFirstName);
You do not use find right. It needs to look at the first index of your array that is passed in.
function firstSearch(item) {
return item[0] === firstInitial;
}
So
var firstNames = [
["A", "Angelic"],
["B", "Blustery"],
["Y", "Cheery"],
["Z", "Dancy"]
];
var firstInitial = window.prompt("What is the first letter of your first name: ").toUpperCase();
const elfFirstName = firstNames.find(firstSearch)[1];
function firstSearch(item) {
return item[0] === firstInitial;
}
window.alert("Your Elf Name is " + elfFirstName);
Better solution as I pointed out in the comments is using an array
var firstNames = {
A: "Angelic",
B: "Blustery",
Y: "Cheery",
Z: "Dancy"
};
var firstInitial = window.prompt("What is the first letter of your first name: ").toUpperCase();
const elfFirstName = firstNames[firstInitial];
window.alert("Your Elf Name is " + elfFirstName);

Discord.js: Pushing to array is combining strings

I am editing an embed message to update users that have "signed up" to a list by reacting. However, after my array length gets to 2, it begins combining the strings of the entire array before adding the new entry. Here is my code:
let newParticipant = getNickname(guildMembers) || user.tag;
//this line exists because "value" cannot be empty in an embed field
//so this removes the placeholder on the first entry
if (newEmbed.fields[2].value[0] === "0") {
newEmbed.fields[2].value = [
`${newEmbed.fields[2].value.length}. ${newParticipant}`,
];
} else {
let participants = [newEmbed.fields[2].value];
let newEntry = participants.length + 1 + ". " + newParticipant;
participants.push(newEntry);
newEmbed.fields[2] = { name: "Participants", value: participants };
console.log(newEmbed.fields[2].value);
}
However this is the output I'm getting after 3 reactions:
[ '1. Cardinal' ]
[ '1. Cardinal', '2. Cardinal' ]
[ '1. Cardinal\n2. Cardinal', '2. Cardinal' ]
Is this something native to discord? Is my logic bad? I've tried using a spread operator when bringing in the array and several other things...
this worked. thank you Worthy Alpaca. I still wish I knew what exactly was happening lol.
let participants = newEmbed.fields[2].value;
let num = participants.split("\n").length;
let newEntry = `\n${num + 1}. ${newParticipant}`;
participants += newEntry;
newEmbed.fields[2] = { name: "Participants", value: participants };
console.log(newEmbed.fields[2].value);

list of strings from array to variable name

I'm grabbing a list of elements ids thusly.
var menus = $(".menu").map(function(){
return this.id;
});
Which returns something like:
["lunch", "appetizers", "soup", "salads", "seafood", "noodles", "stir_fry", "curry", "kids", "steak", "dessert", "sides"]
For each item in the array I want to grab some JSON data.
$.each(menus,function(i) {
var list = menus[i],
meal = data.menu.list,
items = '<li><h3>' + meal.name + '</h3><p>' + meal.desc + '</p></li>';
$('#'+list+".menu").append(items);
});
Such that data.menu.list would be data.menu.lunch, data.menu.appetizers, etc.
The JSON is structured like so:
{
"menu": {
"lunch": [{
"name": "Kao PAdd",
"desc": "Fried rice with onions, green onions, snow peas, and egg / Chicken, vegetarian / Shrimp or tofu (Add $1)"
}
Any thoughts that don't involve eval()?
EDIT:
I when I do this:
$.each(data.menu,function(i) {
console.log(data.menu[i].key);
});
the console gives me:
Object {lunch: Array(14), appetizer: Array(11)}
All I really want is to access those arrays.
console.log(data.menu[i].name)
gives me a pair of undefineds.
That’s a brilliant question, Sir!
No matter how you retrieve your menus, strToVar() will do the task.
This code converts strings from array to variable names:
Solution:
var strToVar = (str,val) => this[str] = val;
Example:
var menus = ["lunch", "appetizers", "soup", "salads", "seafood", "noodles",
"stir_fry", "curry", "kids", "steak", "dessert", "sides"];
menus.forEach(strToVar);
prompt("[lunch, appetizers, soup, salads, seafood, noodles, " +
"stir_fry, curry, kids, steak, dessert, sides]",
[lunch, appetizers, soup, salads, seafood, noodles,
stir_fry, curry, kids, steak, dessert, sides]);
Give me all your points.
If you're looking for parsing JSON string to object here you go:
var jsonString = '{"data":{"item":{"id":1,"value":"foo"}}}';
var jsonObj = JSON.parse(jsonString);
console.log(jsonObj.data.item.value);
The problem was, I didn't understand what I really wanted to do. I asked the wrong question (although it's an interesting one, so I'll leave it up).
I thought I needed to generate my variable list from the HTML ids, but that was a mistake. What I needed was simply another for loop (or jQuery each());
$.each(data.menu, function(i) {
var list = data.menu[i],
menus = [];
$.each(list, function(x) {
var items = '<li><h3>' + list[x].name + '</h3><p>' + list[x].desc + '</p></li>';
menus.push(items)
});
$('#' + i).append(menus);
});
Your $.each function should be:
$.each(menus, function(i, list) { // the second parameter is list so we don't need menus[i]
var meal = data.menu[list], // use of bracket notation
items = '<li><h3>' + meal.name + '</h3><p>' + meal.desc + '</p></li>';
$('#' + list).append(items);
// ^^^ no need for the ".menu" as IDs alone are sufficient (assuming you have unique IDs, otherwise you have a problem)
});
Docs on MDN for bracket notation.
As per my understanding you want to achieve something like this :
var menus = ["lunch", "appetizers", "soup", "salads"];
var menuList = [
{
"name":"lunch",
"description":"description1"
},
{
"name":"appetizers",
"description":"description2"
},
{
"name":"soup",
"description":"description3"
},
{
"name":"salads",
"description":"description4"
}
]
var menu = {};
for(var i in menus) {
menu[menus[i]] = [{
"name": menuList[i].name,
"desc": menuList[i].description
}];
}
console.log(menu);

Compare list of duplicate emails using lodash underscore

I will try to explain what I need to do, why it is not so simple to explain.
I have 4 email lists, and in them there are duplicate items among them, need to extract these emails, for example:
list 1:
email1, email2, email4, email5
list 2:
email2, email3, email5, email6
list 3:
email1, email2, email7, email8
list 4:
email2, email4, email6, email7
I need this:
Result:
email1 (list1, list3)
email2 (list1, list2, list3, list4)
email3 (list2)
....
I'm trying to do this with javascript, because in fact these emails are in an excel ..
JSbin:
http://jsbin.com/zerexuviya/edit?html,js,console,output
Here is the solution I've just wrote using only Underscore.js: https://jsfiddle.net/alienpavlov/b0zaz5Lw/
var all = {
"list1": [
"email1",
"email2",
"email3",
"email4",
"email5",
"email6"
],
"list2": [
"email2",
"email3",
"email6",
"email7"
],
"list3": [
"email1",
"email3",
"email4",
"email5",
"email6"
],
"list4": [
"email1",
"email3",
"email5",
"email6"
]
};
var listOfAllEmails = _.union.apply(undefined, _.values(all));
_.each(listOfAllEmails, function(email, i) {
var result = [];
_.each(all, function(list, j) {
if (_.indexOf(list, email) > -1) {
result.push(j);
}
});
if (result.length > 1) {
//console.log(email, "=>", result);
var div = document.getElementById("result")
div.innerHTML = div.innerHTML + email + " (" + result + ")<br>";
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
<div id="result"></div>
It was all about sorting arrays. If you have any questions feel free to ask in comments.

Populate a list (not HTML) from JSON

I am trying to use wordcloud2.js to generate some word clouds. It works fine using the example given:
var options =
{
list : [
["Pear", "9"],
["Grape", "3"],
["Pineapple", "8"],
["Apple", "5"]
],
gridSize: Math.round(16 * document.getElementById('canvas_cloud').offsetWidth / 1024),
weightFactor: function (size) {
return Math.pow(size, 1.9) * document.getElementById('canvas_cloud').offsetWidth / 1024;
}
}
WordCloud(document.getElementById('canvas_cloud'), options);
However, I am struggling with populating "list :" with the data from a JSON file with the following structure:
[
{
"wordCloud": "Manchester",
"Freq": 2321
},
{
"wordCloud": "Munich",
"Freq": 566
},
{
...
},
{
"wordCloud": "Madrid",
"Freq": 6
}
]
I know it is because my limited knowledges on pushing values to arrays. So far, my tries have been:
$.getJSON('json/wordCloudGWT-' + site + '.json', function (data) {
var wordCloudGWT = [];
for (var i=0;i<100;i++) {
wordCloudGWT.push([data[i].wordCloud, data[i].Freq]);
};
console.log(wordCloudGWT);
var options =
{
list : wordCloudGWT,
gridSize: Math.round(16 * document.getElementById('canvas_cloud').offsetWidth / 1024),
weightFactor: function (size) {
return Math.pow(size, 1.9) * document.getElementById('canvas_cloud').offsetWidth / 1024;
}
}
WordCloud(document.getElementById('canvas_cloud'), options);
console.log(wordCloudGWT); shows an array with 100 (objects?) with a length of 2 each, but the wordcloud doesn't show. I see in my browser's console an error which I assume is because wordcloud2.js is not interpreting list : wordCloudGWT as I (erroneously) think it should be.
If I bruteforce the creation of the list this way
list : [
[data[0].wordCloud, "9"],
[data[1].wordCloud, "3"],
[data[2].wordCloud, "8"],
[data[3].wordCloud, "5"]
],
the words are shown correctly, but this approach has two problems:
Real frequency of words (word's size) is not considered
There is surelly more elegant ways to generate the list as manually adding 100 lines of code
For the first point, I figured that I could solve the problem by also manually editing the list this way:
list : [
[data[0].wordCloud, data[0].Freq],
[data[1].wordCloud, data[1].Freq],
[data[2].wordCloud, data[2].Freq],
[data[3].wordCloud, data[3].Freq]
],
However, doing that ends with the same JS error from my first attempt.
Any hint that can help me to bypass my difficulties?
You can use Array.map to format the data:
var formattedList = responseData.map(function(item) {
return [item.wordCloud, item.Freq]
});
Demo: http://jsfiddle.net/64v75enq/
It seems like the wordcloud2.js library expects the frequency value to be a string, and that is not the case of your json data. So improving on your own example code; add a toString() at the end of the Freq variable when pushing the values. Like this:
list : [
[data[0].wordCloud, data[0].Freq.toString()],
[data[1].wordCloud, data[1].Freq.toString()],
...
]

Categories