SQL style JOIN on JSON data - javascript

Is there any way efficiently to join JSON data? Suppose we have two JSON datasets:
{"COLORS":[[1,red],[2,yellow],[3,orange]]}
{"FRUITS":[[1,apple],[2,banana],[3,orange]]}
And I want to turn this into the following client side:
{"NEW_FRUITS":[[1,apple,red],[2,banana,yellow],[3,orange,orange]]}
Keep in mind there will be thousands of records here with much more complex data structures. jQuery and vanilla javascript are both fine. Also keep in mind that there may be colors without fruits and fruits without colors.
NOTE: For the sake of simplicity, let's say that the two datasets are both in the same order, but the second dataset may have gaps.

Alasql JavaScript SQL library does exactly what you need in one line:
<script src="alasql.min.js"></script>
<script>
var data = { COLORS: [[1,"red"],[2,"yellow"],[3,"orange"]],
FRUITS: [[1,"apple"],[2,"banana"],[3,"orange"]]};
data.NEW_FRUITS = alasql('SELECT MATRIX COLORS.[0], COLORS.[1], FRUITS.[1] AS [2] \
FROM ? AS COLORS JOIN ? AS FRUITS ON COLORS.[0] = FRUITS.[0]',
[data.COLORS, data.FRUITS]);
</script>
You can play with this example in jsFiddle.
This is a SQL expression, where:
SELECT - select operator
MATRIX - modifier, whci converts resultset from array of objects to array of arrays
COLORS.[0] - first column of COLORS array, etc.
FRUITS.1 AS 2 - the second column of array FRUITS will be stored as third column in resulting recordset
FROM ? AS COLORS - data array from parameters named COLORS in SQL statement
JOIN ? ON ... - join
[data.COLORS, data.FRUITS] - parameters with data arrays

The fact that there will be thousands of inputs and the keys are not necessarily ordered means your best bet (at least for large objects) is to sort by key first. For objects of size less than about 5 or so, a brute-force n^2 approach should suffice.
Then you can write out the result by walking through the two arrays in parallel, appending new "records" to your output as you go. This sort-then-merge idea is a relatively powerful one and is used frequently. If you do not want to sort first, you can add elements to a priority queue, merging as you go. The sort-then-merge approach is conceptually simpler to code perhaps; if performance matters you should do some profiling.
For colors-without-fruits and fruits-without-colors, I assume writing null for the missing value is sufficient. If the same key appears more than once in either color or fruit, you can either choose one arbitrarily, or throw an exception.
ADDENDUM I did a fiddle as well: http://jsfiddle.net/LuLMz/. It makes no assumptions on the order of the keys nor any assumptions on the relative lengths of the arrays. The only assumptions are the names of the fields and the fact that each subarray has two elements.

There is not a direct way, but you can write logic to get a combined object like this. Since "apple, red, banana...." are all strings, they should be wrapped in a single or double quote.
If you can match the COLORS and FRUITS config array by adding null values for missing items then you can use this approach.
Working demo
var colors = {"COLORS":[[1,'red'],[2,'yellow'],[3,'orange']]}
var fruits = {"FRUITS":[[1,'apple'],[2,'banana'],[3,'orange']]}
var newFruits = {"NEW_FRUITS": [] }
//Just to make sure both arrays are the same size, otherwise the logic will break
if(colors.COLORS.length == fruits.FRUITS.length){
var temp;
$.each(fruits.FRUITS, function(i){
temp = this;
temp.push(colors.COLORS[i][2]);
newFruits.NEW_FRUITS.push(temp);
});
}
Alternatively, if you can create colors and fruits configs as an array of objects, instead of an array of arrays, you can try this solution. The sequence of the elements is irrelevant here, but the array size should still match.
Working demo
var colors = {"COLORS":[ {"1": 'red'}, { "2": 'yellow'}, {"3":'orange'}]}
var fruits = {"FRUITS":[ {"1":'apple'}, { "2": 'banana'}, {"3":'orange'}]}
var newFruits = {"NEW_FRUITS": [] }
if(colors.COLORS.length == fruits.FRUITS.length){
var temp, first;
$.each(fruits.FRUITS, function(i){
for(first in this)break;
temp = {};
temp[first] = [];
temp[first].push(this[first]);
temp[first].push(colors.COLORS[i][first]);
newFruits.NEW_FRUITS.push(temp);
});
}

Related

Javascript making combinations with an array

I am trying to find all combinations and create an array of string from an array. For example:
var text_array = [["h"],["e","è","é","ê","ë"],["l"],["l"],["o","ò","ó","ô","õ"]]
output = ["hello","hèllo","héllo",etc...]
I have tried several ways but they all seemed extremely long winded and I think I'm just maybe missing a function I don't know about.
One way to abstract the implementation from the number of characters is to incrementally build the list of final values and the values themselves. You start with an empty result, [""]. Then you take the first list of variants and add each variant to every result we have so far, producing a new list of intermediate results, ["h"].
The second list of variants has multiple elements, so after this iteration you'll have this list of results, ["he", "hè", "hé", "hê", "hë"]. And so on.
In pseudo-code, it could look like this:
results = [""]
for each list_of_variants in text_array {
new_results = []
for each variant in list_of_variants {
for each result in results {
new_result.push(result + variant)
}
}
results = new_results
}

Use multiple key-value filters on an object of objects?

Bit of a lengthy one so those of you who like a challenge (or I'm simply not knowledgeable enough - hopefully it's an easy solution!) read on!
(skip to the actual question part to skip the explanation and what I've tried)
Problem
I have a site that has a dataset that contains an object with multiple objects inside. Each of those objects contains an array, and within that array there are multiple objects. (yes this is painful but its from an API and I need to use this dataset without changing or modifying it.) I am trying to filter the dataset based of the key-value pairs in the final object. However, I have multiple filters being executed at once.
Example of Path before looping which retrieves the key-value pair needed for one hall.
["Hamilton Hall"]["Hire Options"][2].Commercial
After Looping Path of required key-value pair for all halls, not just one (the hall identifier is stored):
[0]["Hire Options"][2].Commercial
Looping allows me to check each hall for a specific key-value pair (kind of like map or forEach, but for an object).
After getting that out of the way back to the question.
How would I go about filtering which of the looped objects are displayed?
What I have Tried
(userInput is defined elsewhere - this happens on a btn click btw)
let results = Object.keys(halls);
for (key of results) {
let weekend = [halls[ `${key}` ][ 'Hire Options' ][4][ 'Weekend function' ]];
if(userInput == weekend) {
outputAll([halls[ `${key}` ]]);
}
}
That filters it fine. However, I run into an issue here. I want to filter by multiple queries, and naturally adding an AND into the if statement doesn't work. I also dont want to have 10 if statements (I have 10+ filters of various data types I need to sort by).
I have recently heard of ternary operators, but do not know enough about them to know if that is the correct thing to do? If so, how? Also had a brief loook at switches, but doesnt seem to look like what I want (correct me if I am wrong.)
Actual Question minus the babble/explanation
Is there a way for me to dynamically modify an if statements conditions? Such as adding or removing conditions of an if statement? Such as if the filter for 'a' is set to off, remove the AND condition for 'a' in the if statement? This would mean that the results would only filter with the active filters.
Any help, comments or 'why haven't you tried this' remark are greatly appreciated!
Thanks!
Just for extra reference, here is the code for retrieving each of the objects from the first object as it loops through them:
(Looping Code)
halls = data[ 'Halls' ];
let results = Object.keys(halls);
for (key of results) {
let arr = [halls[ `${key}` ]];
outputAll(arr);
}
You can use Array.filter on the keys array - you can structure the logic for a match how you like - just make it return true if a match is found and the element needs to be displayed.
let results = Object.keys(halls);
results.filter(key => {
if (userInput == halls[key]['Hire Options'][4]['Weekend function']) {
return true;
}
if (some other condition you want to match) {
return true;
}
return false;
}).forEach(key => outputAll([halls[key]]));

Sorting a dynamically filled array of objects

I have an array that is initialized like such var generationObject = [{string:"", score: 0}];
which I then fill dynamically:
for(var i = 0; i < amount_offspring; i++)
{
// "load" text into array and send the string to see if it evolves
generationObject[i].string = evolve(start_text, characters, mutation_rate);
// then score the string
generationObject[i].score = score(target_text, generationObject.string);
}
I then want to sort this array by score. I don't know what's best, to sort it in the for loop or sort the entire array afterwards.
I will then take the string of the highest scoring object and pass it through the function again, recursively.
So what would be a good way to go about this sort function? I've seen some here use this
generationObject.sort(function(a, b) {
return (a.score) - (b.score);
});
But I'm not sure if .sort is still supported? This didnt seem to work for me though.
generationObject is an array, not an object, so score(target_text, generationObject.string); could be the problem, as .string will be undefined. (Did you mean generationObject[i].string?)
Try building your array like this:
var generationObject = []
for(var i = 0; i < amount_offspring; i++)
{
evolved_string = evolve(start_text, characters, mutation_rate)
generationObject.push({
string: evolved_string,
score: score(target_text, evolved_string)
})
}
And then Array.prototype.sort should do the trick.
You should write your sorting logic outside the for loop, since if you put it inside, the object array will be sorted N times, where N being the iterations of your loop. The following are two ways to do it-
By using sort() function- To clarify your question, sort() is still supported across almost all the browsers. If you are still concerned about the browser compatibility, you can check the MDN documentation to see the list of supported browsers.
generationObject = generationObject.sort(function(a, b) {
return parseInt(a.score) - parseInt(b.score);
});
By using underscorejs-
In underscore, you can take advantage of the sortBy() function.
Returns a (stably) sorted copy of list, ranked in ascending order by the results of running each value through iteratee. iteratee may also be the string name of the property to sort by (eg. length).
You can simply do this in underscorejs-
generationObject = _.sortBy(generationObj, 'score');

JavaScript Multidimensional arrays - column to row

Is it possible to turn a column of a multidimensional array to row using JavaScript (maybe Jquery)? (without looping through it)
so in the example below:
var data = new Array();
//data is a 2D array
data.push([name1,id1,major1]);
data.push([name2,id2,major2]);
data.push([name3,id3,major3]);
//etc..
Is possible to get a list of IDs from data without looping? thanks
No, it is not possible to construct an array of IDs without looping.
In case you were wondering, you'd do it like this:
var ids = [];
for(var i = 0; i < data.length; i++)
ids.push(data[i][1]);
For better structural integrity, I'd suggest using an array of objects, like so:
data.push({"name": name1, "id": id1, "major":major1});
data.push({"name": name2, "id": id2, "major":major2});
data.push({"name": name3, "id": id3, "major":major3});
Then iterate through it like so:
var ids = [];
for(var i = 0; i < data.length; i++)
ids.push(data[i].id);
JavaScript doesn't really have multidimensional arrays. What JavaScript allows you to have is an array of arrays, with which you can interact as if it was a multidimensional array.
As for your main question, no, you would have to loop through the array to get the list of IDs. It means that such an operation cannot be done faster than in linear time O(n), where n is the height of the "2D array".
Also keep in mind that arrays in JavaScript are not necessarily represented in memory as contiguous blocks. Therefore any fast operations that you might be familiar with in other low level languages will not apply. The JavaScript programmer should treat arrays as Hash Tables, where the elements are simply key/value pairs, and the keys are the indices (0, 1, 2...). You can still access/write elements in constant time O(1) (at least in modern JavaScript engines), but copying of elements will often be done in O(n).
You could use the Array map function which does the looping for you:
var ids = data.map(function(x) { return x[1] });
Unfortunately, like everything else on the web that would be really nice to use, INTERNET EXPLORER DOESN'T SUPPORT IT.
See this page for details on how the map function works:
https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/map
The good news it that the link above provides some nice code in the "Compatibility" section which will check for the existence of Array.prototype.map and define it if it's missing.
You don't need anything special- make a string by joining with newlines, and match the middle of each line.
var data1=[['Tom Swift','gf102387','Electronic Arts'],
['Bob White','ea3784567','Culinarey Arts'],
['Frank Open','bc87987','Janitorial Arts'],
['Sam Sneer','qw10214','Some Other Arts']];
data1.join('\n').match(/([^,]+)(?=,[^,]+\n)/g)
/* returned value: (Array)
gf102387,ea3784567,bc87987
*/

Does JavaScript populate empty array items?

I am coding a lot of annual data in JavaScript, and I was considering adding it to arrays, using the year as the array index and putting the data into the array. However, Firebug seems to be indicating that JavaScript handles this by populating two thousand odd entries in the array with "undefined." With hundreds of such arrays kicking around in active memory, I'm worried the overhead of hundreds of thousands of useless array items could start to slow the program down. Will it?
When you set the value of a numeric index higher than the current length of your array, the length property is affected.
In brief, you should use an Object:
var data = {};
data[year] = "some data";
// or
var data = {
2009: "2009 data",
2010: "2010 data"
};
Now I answer the question title: "Does JavaScript populate empty array items?"
No, as I said before, only the length property is changed, (if necessary, only if the index added is larger than the current length), length is incremented to be one more than the numeric value of that index.
The Array.prototype methods work assuming that the array object will have its indexes starting from zero.
The previous indexes don't really exist in the Array object, you can test it:
var array = [];
array[10] = undefined;
array.hasOwnProperty(10); // true
array.hasOwnProperty(9); // false
In conclusion, arrays are meant to contain sequential indexes, starting from zero, if your properties don't meet those requirements, you should simply use an object.
Yes, most likely. You should consider using a JavaScript object instead:
var years = {2009: 'Good', 2010: 'Better'};
Well, if you iterate over many thousands of undefined, it will affect overall program speed, not sure if you'll notice it though.
On the other hand, sometimes a sparse array is simpler to use than a custom object,
and arrays have such handy methods available.
In a calendar application I begin with objects for each year in use, but each year consists of a twelve member (months array) and each 'month' is a sparse array of significant dates, whose lengths depend on the highest date of that month that has any data.

Categories