Building an object graph from flat object array in javascript - javascript

i have a javascript array of objects with objects that look like this:
itemId
name
parentItemId <== top level items with no parent have null value
I want to build a graph where the parent items contain arrays of children and those children have arrays of children if applicable.
What is a good way to go about this?

function objectGraph(items)
{
var items_by_id = {};
var roots = [];
var i;
// Build an id->object mapping, so we don't have to go hunting for parents
for (i = 0; i < items.length; ++i) {
items_by_id[items[i].itemId] = items[i];
items[i].children = [];
}
for (i = 0; i < items.length; ++i) {
var parentId = items[i].parentItemId;
// If parentId is null, this is a root; otherwise, it's parentId's kid
var nodes = (parentId === null) ? roots : items_by_id[parentId].children;
nodes.push(items[i]);
}
return roots;
}
Note, this code gives every node a children property, that's empty if a node has no kids. I personally find it simpler and more consistent than each node maybe-or-maybe-not having children; you can loop over children without worrying whether it exists. A leaf node will have children.length == 0.
If you can guarantee you have exactly one root, you can return roots[0]; instead of returning the array.

When you build a "tree builder function" you have to decide if the "top level thing" is a single item or an array of items. Since you said itemS we go with an array. The difference is the parameter you pass in and get returned back, if its an array we pass the parentId, otherwise we pass the id.
function buildTree(parentId, list) {
var nodes = [];
for (var i=0, l; l = list[i]; i++) {
if (l.parentId === parentId) {
// if you need "myList" intact afterwards remove the next line at the cost of efficiency
list.splice(i, 1); i--;
nodes.push({
id: l.id
,parentId: l.parentId
,name: l.name
,children: buildTree(l.id, list)
});
}
}
return nodes;
}
var myTree = buildTree(null, myList);

This is a little rough, but it should do the job if I'm gathering your question correctly. It should return an array of top level objects that have their children correctly organized below them.
As a note, this will work for an N-level of children objects, and not just a single level.
var finalArray = [];
var YOUR_RAW_ARRAY = [];
var buildObjectGraph = function(inputArray){
var i = 0, len = inputArray.length;
var returnVal = [];
for(;i<len;i++){
if(inputArray[i].parentItemId === null){
findChildren(inputArray[i], inputArray);
returnVal.push(inputArray[i]);
}
}
var findChildren = function(root){
var i = 0, i2 = 0, len = rawDataArray.length, len2 = 0;
for(;i<len;i++){
if(inputArray[i].parentItemId === root.itemId){
if(root.children){
root.children.push(inputArray[i]);
}else{
root.children = [];
root.children.push(inputArray[i]);
}
}
}
//now call it recursively
len2 = root.children.length;
if(len2 > 0){
for(;i2 < len2; i2++){
findChildren(root.children[i2]);
}
}
};
return returnVal;
};
//Then execute it
finalArray = buildObjectGraph(YOUR_RAW_ARRAY);

Related

Jquery/Javascript removing entry from array with highest property value

I have a nice riddle that I would like to see solved. There might be a better way of doing this and i am open for idea's.
I am trying to write an undo function for a canvas drawing app.
I have the following object, within it an array with their own objects with three properties.
var allDamages= {};
allDamages['scratch'] = [];
allDamages['scratch'].push({"x":4,"y":6,"index":1});
allDamages['scratch'].push({"x":3,"y":3,"index":2});
allDamages['scratch'].push({"x":9,"y":9,"index":3});
allDamages['scratch'].push({"x":19,"y":39,"index":4});
allDamages['dent'] = [];
allDamages['dent'].push({"x":59,"y":69,"index":5});
allDamages['dent'].push({"x":59,"y":69,"index":9});
allDamages['dent'].push({"x":39,"y":19,"index":6});
allDamages['rip'] = [];
allDamages['rip'].push({"x":20,"y":22,"index":7});
allDamages['rip'].push({"x":100,"y":56,"index":8});
I want to remove the last entry from this array. I want to do this by the property 'index'.
So I need to somehow find the entry which has the highest value of the property 'index' and then remove it from the array. What is the best way in doing this?
Greetings,
Robert
allDamages.scratch.length -1 returns the last index for that array.
Edit:
allDamages.scratch.slice(-1).pop() returns the last array item.
And if you just want to remove the last item in your array you should (like Givi said) use the pop() method on a sorted array like so:
allDamages['scratch'].pop()
Edit2:
Because the question wasn't clear for me. This is my final shot at the problem.
var allDamagesInOneArray = [];
for(array in allDamages){
allDamagesInOneArray.concat(array);//Assuming every key is an array
}
allDamagesInOneArray.sort(function(a,b){
return a.index - b.index;
});
var lastObj = allDamagesInOneArray.slice(-1).pop(); //element with latest index
I think you should create an object that save three your properties. After that you create a stack for undo. Like this:
function yourObject(x,y,index){
this.x = x; this.y = y; this.index = index;
}
var yourStack = new Array();
yourStack.push(new yourObject(4, 6, 1));
If the highest index in an array is always the last element of the array:
allDamages.scratch = allDamages.scratch.slice(0, allDamages.scratch.length - 1);
This removes the last element of the array
If index is not incrementing or if you always want to remove the latest index, no matter in which of the damages arrays it is (as I'd guess) you can use this function:
var undo = function(input){
var max= 0;
var undoType = "";
var undoIndex = 0;
for( var type in input ) {
// type: string
var locations = input[type];
// locations: array
// find the location of the heighest index property.
for( var i = 0; i < locations.length; i++ ) {
if( locations[i]["index"] > max) {
max = locations[i]["index"] ;
undoType = type;
undoIndex = index;
}
}
}
var output = input[type].splice(undoIndex, 1);
return output;
}
This should remove the element with the largest "index" property from your damage array.
First off, store a counter for highest index property found in the objects, and the index of that object within the scratch array.
var highestIndex = -Infinity;
var indexInArray
Then if you're using jQuery:
$.each( allDamages.scratch, function highestIndex( index, object ){
if( object.index > highestIndex ){
highestIndex = object.index;
indexInArray = index;
}
} );
Or, if not:
for( var indexCounter = 0, indexCounter < allDamages.scratch, indexCounter++ ){
if( allDamanges.scratch[ indexCounter ].index > highestIndex ){
highestIndex = allDamages.scratch[ indexCounter ].index;
indexInArray = indexCounter;
}
};
Try:
var allDamages= {};
allDamages['scratch'] = [];
allDamages['scratch'].push({"x":4,"y":6,"index":1});
allDamages['scratch'].push({"x":3,"y":3,"index":2});
allDamages['scratch'].push({"x":9,"y":9,"index":3});
allDamages['scratch'].push({"x":19,"y":39,"index":4});
allDamages['dent'] = [];
allDamages['dent'].push({"x":59,"y":69,"index":5});
allDamages['dent'].push({"x":59,"y":69,"index":9});
allDamages['dent'].push({"x":39,"y":19,"index":6});
allDamages['rip'] = [];
allDamages['rip'].push({"x":20,"y":22,"index":7});
allDamages['rip'].push({"x":100,"y":56,"index":8});
var index;
var cnt = 0;
var val;
$.each(allDamages,function(k,v){
if(cnt == 0){
index = highest(v); //get highest value from each object of allDamages
val = k;
}
else{
if(highest(v) > index){
index = highest(v);
val = k;
}
}
cnt++;
});
console.log("highest : "+index+": "+val);
var len = allDamages[val].length;
for(var i=0;i<len;i++){
if(allDamages[val][i].index == index){
allDamages[val].splice(i,1); //remove object having highest value
break;
}
}
console.log(allDamages);
function highest(ary) {
var high = ary[0].index;
var len = ary.length;
if(len > 0){
for(var i=0;i<len;i++){
if(ary[i].index > high){
high = ary[i].index;
}
}
}
return high;
}
DEMO here.
I've simplified my array to:
allDamages.push({"x":39,"y":19,"index":6,"type":'dent'});
That way i can use .pop() function in a normal way.
Thank you all for the quick response!!!

How to show a list or array into a tree structure in javascript?

I pass this list from python to javascript like this:
var string=["test_data/new_directory/ok.txt","test_data/reads_1.fq","test_data/test_ref.fa"];
I want output like this:
test_data
reads_1.fq
test_ref.fa
new_directory
ok.txt
Or also the output could be like this:
test_data
reads_1.fq
test_ref.fa
test_data/new_directory
ok.txt
I used split function to get a list with each file and directory like this:
var string=["test_data/new_directory/ok.txt","test_data/reads_1.fq","test_data/test_ref.fa"];
for(var i=0;i<string.length;i++){
var result = string[i].split('/');
console.log(result);
}
Output looks like this:
["test_data", "new_directory", "ok.txt"]
["test_data", "reads_1.fq"]
["test_data", "test_ref.fa"]
How can I convert into the format I showed above? Thanks
Sorry for being late to the party. I ran into a similar issue trying to break out a list of paths into a nested object. Here's a fiddle showing how I ended up doing it.
var list = [];
list.push("A/B/C");
list.push("A/B/D");
list.push("A/B/C");
list.push("B/D/E");
list.push("D/B/E");
list.push("A/D/C");
var data = [];
for(var i = 0 ; i< list.length; i++)
{
buildTree(list[i].split('/'),data);
}
debugger;
function buildTree(parts,treeNode) {
if(parts.length === 0)
{
return;
}
for(var i = 0 ; i < treeNode.length; i++)
{
if(parts[0] == treeNode[i].text)
{
buildTree(parts.splice(1,parts.length),treeNode[i].children);
return;
}
}
var newNode = {'text': parts[0] ,'children':[]};
treeNode.push(newNode);
buildTree(parts.splice(1,parts.length),newNode.children);
}
https://jsfiddle.net/z07q8omt/
That's certainly possible, but it requires recursion.
The first thing you'll want to do (as you've already figured out to do, in fact) is split on the slashes. We'll use map for simplicity:
paths = paths.map(function(path) { return path.split('/'); });
Now we'll want to convert this into an array of objects with name and children properties. This means we'll have to use recursion.
In this function, we'll do a first pass grouping them by their first element:
var items = [];
for(var i = 0, l = paths.length; i < l; i++) {
var path = paths[i];
var name = path[0];
var rest = path.slice(1);
var item = null;
for(var j = 0, m = items.length; j < m; j++) {
if(items[j].name === name) {
item = items[j];
break;
}
}
if(item === null) {
item = {name: name, children: []};
items.push(item);
}
if(rest.length > 0) {
item.children.push(rest);
}
}
Then we can recurse on all of these (assuming the function name we chose was structurize):
for(i = 0, l = items.length; i < l; i++) {
item = items[i];
item.children = structurize(item.children);
}
Now we've got a nice structure. We can then stringify it, again with a recursive function. Since the directory listing is just each item name followed by the indented directory contents listing, we can write that fairly easily:
function stringify(items) {
var lines = [];
for(var i = 0, l = items.length; i < l; i++) {
var item = items[i];
lines.push(item.name);
var subLines = stringify(item.children);
for(var j = 0, m = subLines.length; j < m; j++) {
lines.push(" " + subLines[j]);
}
}
return lines;
}
Then, to actually do it:
console.log(stringify(structurize(paths)).join("\n"));

Count how many strings in an array have duplicates in the same array [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Array value count javascript
I have an array which contains several duplicates, what I'm trying to achieve is to count how many duplicates each unique string has in this one array.
The array looks something like this
array = ['aa','bb','cc','aa','ss','aa','bb'];
Thus I would like to do something like this
if (xWordOccurrences >= 5) {
// do something
}
But I'm not sure how I would code this.
I was thinking, create an object with each unique string, then loop through the original array, match each string with it's object and increment it's number by 1, then loop over the object to see which words had the most duplicates...
But this seems like an over complexe way to do it.
You can use an object which has keys of the Array's values and do something like this
// count everything
function getCounts(arr) {
var i = arr.length, // var to loop over
obj = {}; // obj to store results
while (i) obj[arr[--i]] = (obj[arr[i]] || 0) + 1; // count occurrences
return obj;
}
// get specific from everything
function getCount(word, arr) {
return getCounts(arr)[word] || 0;
}
getCount('aa', ['aa','bb','cc','aa','ss','aa','bb']);
// 3
If you only ever want to get one, then it'd be more a bit more efficient to use a modified version of getCounts which looks similar to getCount, I'll call it getCount2
function getCount2(word, arr) {
var i = arr.length, // var to loop over
j = 0; // number of hits
while (i) if (arr[--i] === word) ++j; // count occurance
return j;
}
getCount2('aa', ['aa','bb','cc','aa','ss','aa','bb']);
// 3
Try this function:
var countOccurrences = function(arr,value){
var len = arr.length;
var occur = 0;
for(var i=0;i<len;i++){
if(arr[i]===value){
occur++;
}
}
return occur;
}
var count = countOccurrences(['aaa','bbb','ccc','bbb','ddd'],'bbb'); //2
If you want, you can also add this function to the Array prototype:
Array.prototype.countOccurrences = function(value){
var len = this.length;
var occur = 0;
for(var i=0;i<len;i++){
if(this[i]===value){
occur++;
}
}
return occur;
}
How about you build an object with named property?
var array = ['aa','bb','cc','aa','ss','aa','bb'];
var summary = {};
var item = '';
for ( i in array){
item = array[i];
if(summary[item]){
summary[item] += 1;
}
else{
summary[item] = 1;
}
}
console.log( summary );
summary will contain like this
{aa: 3, bb: 2, cc: 1, ss: 1}
which you could then iterate on and then sort them later on if needed.
finally to get your count, you could use this summary['aa']
<script type="text/javascript">
var array = ['aa','bb','cc','aa','ss','aa','bb'];
var myMap = {};
for(i = 0; i < array.length; i++) {
var count = myMap[array[i]];
if(count != null) {
count++;
} else {
count = 1;
}
myMap[array[i]] = count;
}
// at this point in the script, the map now contains each unique array item and a count of its entries
</script>
Hope this solves your problem
var array = ['aa','bb','cc','aa','ss','aa','bb'];
var dups = {};
for (var i = 0, l = array.length; i < l; i++ ) {
dups[array[i]] = [];
}
for (str in dups) {
for (var i = 0, l = array.length; i < l; i++ ) {
if (str === array[i]) {
dups[str].push(str);
}
}
}
for (str in dups) {
console.log(str + ' has ' + (dups[str].length - 1) + ' duplicate(s)');
}
This function may do everything you need.
function countDupStr(arr, specifier) {
var count = {}, total = 0;
arr.forEach(function (v) {
count[v] = (count[v] || 0) + 1;
});
if(typeof specifier !== 'undefined') {
return count[specifier] - 1;
}
Object.keys(count).forEach(function (k) {
total += count[k] - 1;
});
return total;
}
Each value in the array is assigned and incremented to the count object. Whether or not a specifier was passed, the function will return duplicates of that specific string or the total number of duplicates. Note that this particular technique will only work on string-coercible values inside your arrays, as Javascript can only index objects by string.
What this means is that during object assignment, the keys will normalize down to strings and cannot be relied upon for uniqueness. That is to say, this function wouldn't be able to discern the difference between duplicates of 3 and '3'. To give an example, if I were to perform:
var o = {}, t = {};
o[t] = 1;
console.log(o);
The key used in place of t would eventually be t.toString(), thus resulting in the perhaps surprising object of {'[object Object]': 1}. Just something to keep in mind when working with Javascript properties.
I saw this post about it, perhaps it can help:
http://ryanbosinger.com/blog/2011/javascript-count-duplicates-in-an-array/

Remove duplicate element pairs from multidimensional array

I have an array that looks like this:
1. coordinates = [ [16.343345, 35.123523],
2. [14.325423, 34.632723],
3. [15.231512, 35.426914],
4. [16.343345, 35.123523],
5. [15.231512, 32.426914] ]
The latitude on line 5 is the same as on line 3, but they have different longitudes and are therefore not duplicates.
Both the latitude and longitude are the same on line 3 and 6, and are therefore duplicates and one should be removed.
The difficulty in this question that different arrays never compare equal even if they contain same values. Therefore direct comparison methods, like indexOf won't work.
The following pattern might be useful to solve this. Write a function (or use a built-in one) that converts arrays to scalar values and checks if these values are unique in a set.
uniq = function(items, key) {
var set = {};
return items.filter(function(item) {
var k = key ? key.apply(item) : item;
return k in set ? false : set[k] = true;
})
}
where key is a "hash" function that convert items (whatever they are) to comparable scalar values. In your particular example, it seems to be enough just to apply Array.join to arrays:
uniqueCoords = uniq(coordinates, [].join)
You can use standard javascript function splice for this.
for(var i = 0; i < coordinates.length; i++) {
for(var j = i + 1; j < coordinates.length; ) {
if(coordinates[i][0] == coordinates[j][0] && coordinates[i][1] == coordinates[j][1])
// Found the same. Remove it.
coordinates.splice(j, 1);
else
// No match. Go ahead.
j++;
}
}
However, if you have thousands of points it will work slowly, than you need to consider to sort values at first, then remove duplicates in one loop.
I rewrote the answer from thg435 (It does not allow me to post comments) and prototype it also using jQuery instead, so this will work on all browsers using it (Even IE7)
Array.prototype.uniq = function (key) {
var set = {};
return $.grep(this, function (item) {
var k = key
? key.apply(item)
: item;
return k in set
? false
: set[k] = true;
});
}
You can use it like:
arr = arr.uniq([].join);
If you are not on Safari this single liner could do the job
var arr = [[16.343345, 35.123523],
[14.325423, 34.632723],
[15.231512, 35.426914],
[16.343345, 35.123523],
[15.231512, 32.426914]],
lut = {},
red = arr.filter(a => lut[a] ? false : lut[a] = true);
document.write("<pre>" + JSON.stringify(red,null,2) + "</pre>");
It might be simpler to create another array keeping only unique coordinate pairs
var uniqueCoors = [];
var doneCoors = [];
for(var x = 0; x < coordinates.length; x++) {
var coorStr = coordinates[x].toString();
if(doneCoors.indexOf(coorStr) != -1) {
// coordinate already exist, ignore
continue;
}
doneCoors.push(coorStr);
uniqueCoors.push(coordinates[x]);
}
function sortCoordinates(arr){
var obj = {};
for(var i = 0, l = arr.length; i < l; i++){
var el = arr[i];
var lat = el[0];
var lng = el[1];
if(!obj[lat + lng]){
obj[lat + lng] = [lat, lng];
}
}
var out = [];
for(p in obj){
out.push([obj[p][0], obj[p][1]]);
}
return out;
}
I am not sure about coordinates[][] dataType. Make the comparison accordingly.
var dubJRows= new Array();
for(int i = 0; i < coordinates.length -2; i++){
for(int j = i+1; j < coordinates.length -1; j++){
if (i != j && chk_dubJRows_not_contains(j)) {
innerArray1 [1][1] = coordinates[i];
innerArray2 [1][1] = coordinates[j];
if ( innerArray1 [1][0] == innerArray2[1][0]
&& innerArray1[1][1] == innerArray2[1][1]) {
dubJRows.push(j);
}
}
}
}
//REMOVE ALL dubJRows from coordinates.

jQuery/JS: Sorting array based upon another array?

I want to sort a JSON object/array (shown below as myArray), upon values from another array - very close to the MYSQL query MYSQL WHERE IN(1,2,3). I was able to get a great answer by Nick Craver on how to sort by one property and value, but how can I do this with multiple values from my other array?
Here's my dataset Json array:
var myArray = [
{
"id":"2",
"name":"My name",
"properties":{"prop1":"value1"}
}];
And the array which I want to sort upon (serialized, coming straight from a form):
var sortArray = [ { "prop1":"value1","prop2":"value2" }];
The current sorting function as it looks right now (courtesy Nick Craver):
function filterDataset(property, value){
var newArray = [];
for (var i = 0, l = myArray.length; i < l; i++) {
if (myArray[i].properties[property] === value)
newArray.push(myArray[i]);
}
return newArray;
}
Here's how I managed to fix it:
function filterDataset2(properties){
var newArray = [];
for (var i = 0, l = dataset.length; i < l; i++) {
$.each(properties, function(){
if (dataset[i].properties[this.name] === this.value)
newArray.push(myArray[i]);
});
}
return newArray;
}
This may not be what you mean, but if you have a known list of properties, could you just || your comparison? Say you have 2 properties...
function filterDataset(property, value){
var newArray = [];
for (var i = 0, l = myArray.length; i < l; i++) {
if ((dataset[i].egenskaper[property1] === value) || (dataset[i].egenskaper[property2] === value) )
newArray.push(myArray[i]);
}
return newArray;
}
Otherwise if the length of the sorting array is unknown you could use a array.find type method that will return true if the property is found within the array in question. If it returns true, just push that value on your newly sorted array.

Categories