make a left outer join in javascript - javascript

I have an array of objects in javascript (D3) and I need to remove every object of which a certain attribute is present in another array of objects attribute,
i.e. a left outer join
(source: tazindeed.co.uk)
I managed to do it myself with 2 loops but it's quite slow.
And I don't know how to make it faster.
for (var i = 0; i < data1.length; i++) {
for (var j = 0; j < data2.length; j++) {
if (data2[j].attr3 == data1[i].attr4) {
data2.splice(j,1);
}
}
}
data1.length~2k and data2.length~10k
I know this has approximately been asked here but it's been almost 2 years and the solutions use external libraries.
I'm just curious to learn if there is a better method with javascript (or jQuery or D3, which I already use)
Thank you for your help !

You need a fast lookup for the values that exist in data1, so make a map using an object:
var map = {};
for (var i = 0; i < data1.length; i++) {
map[data1[i].attr4] = 1;
}
Then you can loop throught the items in data2 and filter them:
var result = [];
for (i = 0; i < data2.length; i++) {
if (!(data2[i].attr3 in map)) {
result.push(data2[i]);
}
}

Maybe not faster but more readable
const left = ['1', '2', '7']
const right = ['1', '3', '5', '9']
const result = left.filter((x) => !right.includes(x))

You could use Array.filter (see MDN) or Array.map for that I suppose:
var array1 = [1,2,3,4,5,6,7,8,9],
array2 = [3,4,5,6],
fltr = array1.filter( function(v) {return this.indexOf(v) < 0;}, array2),
map = array1.map(
function(v) {
var exists = this.indexOf(v);
return [v, (this[exists] || 'null')].join(', ');},
array2),
result = document.querySelector('#result');
fltr.unshift('<u>array1</u>');
map.unshift('<u>array1, array2</u>');
result.innerHTML = ['<b>filtered</b>\n',
fltr.join('\n'),
'\n\n<b>mapped</b>\n',
map.join('\n')].join('');
<pre id="result"></pre>

Related

JavaScript - Combine two arrays item by item

I have two arrays of arrays where the first contains the location name and the second contains the location latitude and longitude values. Each item in each array corresponds to it's counterpart in the other and both arrays have the same number of items like so:
var arr1 = [['Location #1'],['Location #2']];
var arr2 = [['36.1978319','-83.02365759999999'],['38.679842','-121.7457402']];
What I need is a combined array of the two sets of items, but not concatenated like so:
var arr3 = [['Location #1','36.1978319','-83.02365759999999'],
['Location #2','38.679842','-121.7457402']];
The only way I can think of doing this would be with like a combined for loop, but I can't get the syntax correct and not sure this is even possible... something like this:
for ((var a = 0; a < arr1.length; a++) && (var b = 0; b < arr2.length; b++)) {
arr3.push(arr1[a] + ',' + arr2[b]);
}
Is there a way to solve this problem with pure javascript?
I suggest Array.map for its shortness:
var arr1 = [['Location #1'], ['Location #2']];
var arr2 = [['36.1978319', '-83.02365759999999'], ['38.679842', '-121.7457402']];
var combined = arr1.map((element, i) => element.concat(arr2[i]));
console.log(combined);
For a more generic solution (combining an arbitrary number of arrays), refer to Javascript equivalent of Python's zip function
If the two arrays really are equal in length, and correspond in index. This is all you need:
for (var a = 0; a < arr1.length; a++) {
arr3.push(arr1[a] + ',' + arr2[a]);
}
You are trying to combine 2 contors inside a for loop :).
You want to do this:
var resultArray = new Array();
for (var i = 0; i < arr1.length; i++) {
resultArray.push([arr1[i], arr2[i][0], arr2[i][1]]);
}

How to generate an array with repeated string javascript

How to generate an array with function like this?
var name = ["monkey","monkey"..."horse","horse",..."dog","dog",..."cat","cat"...]​
In my real case, I may have to repeat each name 100 times..
Assuming that you already have that words in a array try this code:
var words = ["monkey", "hourse", "dog", "cat"];
var repeatWords = [];
for(var i = 0; i < words.length; i++)
{
for(var j = 0; j < 100; j++)
{
repeatWords.push(words[i]);
}
}
You can try this, specifying the words to be used, and the times to create the array you need.
var neededWords = ["Cat", "Hourse", "Dog"];
var finalArray = [];
var times = 10;
for (var i = 0; i < neededWords.length; i++) {
for (var n = 0; n < times; n++) {
finalArray.push(neededWords[i]);
}
}
console.log(finalArray);
Hope that helps!
If I understood correctly you need a function that takes as an argument a collection of items and returns a collection of those items repeated. From your problem statement, I assumed that the repetition has to be adjusted by you per collection item - correct me if I am wrong.
The function I wrote does just that; it takes an object literal {name1:frequency1,name2:frequency2..} which then iterates over the keys and pushes each one as many times as indicated by the associated frequency in the frequencyMap object.
function getRepeatedNames( frequencyMap ) {
var namesCollection = [];
Object.keys(frequencyMap).forEach(function(name,i,names){
var freq = frequencyMap[name];
freq = (isFinite(freq)) ? Math.abs(Math.floor(freq)) : 1;
for (var nameCounter=0; nameCounter<freq; nameCounter++) {
namesCollection.push(name);
}
});
return namesCollection;
}
Non-numeric values in the frequency map are ignored and replaced with 1.
Usage example: If we want to create an array with 5 cats and 3 dogs we need to invoke
getRepeatedNames({cat: 2, dog: 3}); // ["cat","cat","dog","dog","dog"]

Get an object from two nested array in Javascript

I want to have an object from two arrays and I did it in the following way.
for (var j = 0; j < rawDataRows.length; j++) {
for (var i = 0; i < categories.length; i++) {
var category = categories[i];
var rowValue = rawDataRows[j];
// here I do got the right value for category
console.log(category);
console.log(rowValue);
// but the following line doesn't interpret category as a variable
formattedDataRows.push({category: rowValue});
}
}
I was assuming I can get something like :
[{"category1": "value1"},{"category2": "value2"}, {"category3": "value3"}]
However, it turned out I got:
[{"category": "value1"}, {"category": "value2"}, {"category": "value3"}]
Can anyone point me where I am wrong? Also, if you have a better way achieving the goal, leave a comment please. Javascript only no jQuery or other framework. Thanks!
Object literal syntax In ECMAScript 5 and lower doesn't allow you to specify a variable identifier as property name. Instead create the object first and then use bracket notation.
var o = {};
o[category] = rowValue;
formattedDataRows.push(o);
With ECMAScript 6, you can do this:
formattedDataRows.push({[category]: rowValue});
though of course support for that syntax is limited at this point in time.
If you want both values to increment together (as it seems), also assuming the length of categories is the same length as rawdataRows, I think you really want a single loop instead of two loops:
for (var i = 0; i < categories.length; i++) {
var category = categories[i];
var rowValue = rawDataRows[i];
You can use categories[i].toString(); to get strings as you wished:
var categories = ["category1", "category2", "category3"];
var rawDataRows = ["value1", "value2", "value3"];
var formattedDataRows = [];
for (var j = 0; j < rawDataRows.length; j++) {
for (var i = 0; i < categories.length; i++) {
var category = categories[i].toString();
var rowValue = rawDataRows[j].toString();
var tmpObj = {}
tmpObj[category] = rowValue
formattedDataRows.push(tmpObj);
//formattedDataRows.push({[category]: rowValue});
}
}
document.write(JSON.stringify(formattedDataRows))

Inserting (splicing?) an Array into another Array without apply

Let's say we have two Arrays in JavaScript, [3,4,7] and [5,6].
Without sorting or using .apply, what is the best way to insert [5,6] into [3,4,7] at index 2 in order to achieve the resulting Array: [3,4,5,6,7]?
Don't know how you're defining "best way", but you can do this:
a.slice(0,2).concat(b,a.slice(2));
Unless you're saying you actually want to mutate the a Array, in which case you could do this:
var c = a.splice(2);
for (var i = 0; i < b.length + c.length; i++) {
a.push(i < b.length ? b[i] : c[i-b.length]);
}
This behavior of .splice() to split the Array in two parts may have issues in older IE that would need to be patched.
Or this would probably be better:
var c = b.concat(a.splice(2));
for (var i = 0; i < c.length; i++) {
a.push(c[i]);
}
Same caveat about .splice().
function splice(arrayOne, arrayTwo, index) {
var result = [];
for (var i = 0; i < arrayOne.length; i++) {
if (i == index) {
result = result.concat(arrayTwo);
}
result.push(arrayOne[i]);
}
return result;
}
Not really sure why you don't want to use the native methods, but here's a fairly naive solution with just loops:
function doInsert(index, items, arr) {
var insertLen = items.length,
current;
for (i = 0; i < insertLen; ++i) {
current = i + index;
arr[current + insertLen - 1] = arr[current];
arr[current] = items[i];
}
}
var arr = [3, 4, 7];
doInsert(2, [5, 6], arr);
console.log(arr);

Setting vars using for loops

I have the following javascript
information0 = xmlDoc.getElementsByTagName("info")[0].textContent;
information1 = xmlDoc.getElementsByTagName("info")[1].textContent;
information2 = xmlDoc.getElementsByTagName("info")[2].textContent;
information3 = xmlDoc.getElementsByTagName("info")[3].textContent;
information4 = xmlDoc.getElementsByTagName("info")[4].textContent;
information5 = xmlDoc.getElementsByTagName("info")[5].textContent;
information6 = xmlDoc.getElementsByTagName("info")[6].textContent;
I want to create a new var for each index number. There are 600 in total. How can I do this using a for loop?
Thanks in advance
The best thing here is to use an array, not a bunch of individual variables.
var information = [];
var index;
var info = xmlDoc.getElementsByTagName("info");
for (index = 0; index < info.length; ++index) {
information[index] = info[index].textContent;
}
Um... use an array? Also, don't call getElementsByTagName repeatedly, it's expensive!
var tags = xmlDoc.getElementsByTagName('info'), l = tags.length, i, information = [];
for( i=0; i<l; i++) information[i] = tags[i].textContent;
If you're in a reasonably up-to-date browser:
var information = [].map.call(xmlDoc.getElementsByTagName('info'),function(a) {return a.textContent;});
Like this:
var information = [],
i,
elements = xmlDoc.getElementsByTagName("info"),
n = elements.length;
for (i = 0; i < n; ++i) {
information[i] = elements[i].textContent;
}
You need to use an array.
var infoTags = xmlDoc.getElementsByTagName("info"),
i = 0,
len = infoTags.length,
values = []; //array literal syntax, you could also use new Array()
for (; i < len; i++) {
values.push(infoTags[i].textContent); //push the textContent into the array
}
Things that you should note:
I cached the result of getElementsByTagName instead of performing the query multiple times.
I cached the length property of infoTags. That avoids multiple property lookups to access infoTags.length on every iterations. Property lookups are expensive in JavaScript.
To know how you can work with arrays, have a look at the Array object.
-

Categories

Resources