Creating series from series in d3 - javascript

I have data of this form (simplified, but assume 20 columns between Admin and Mining):
Date,Series,Admin,Mining,CPI
1990,Ordinary Time Earnings,20,30,96
1991,Ordinary Time Earnings,22,33,100
1990,Total Earnings,25,38,96
1991,Total Earnings,29,43,100
Which I separate out into two series like this:
d3.csv("avgearnings_v1_1.csv", function(error, data) {
if (error) throw error;
OrdinaryTimeEarnings = data
.filter(function(d) {
if(d.Series == 'Ordinary Time Earnings')
return d;
});
TotalEarnings = data
.filter(function(d) {
if(d.Series == "Total Earnings")
return d;
});
And can get that to display on a graph without any issue. What I want to do next is create two more series:
OrdinaryTimeEarningsReal = OrdinaryTimeEarnings;
TotalEarningsReal = TotalEarnings;
And then recalculate those new series. Basically:
For any column that is not Date/Series/CPI
Take the CPI value for that year
Individually divide each of the Mining-Admin columns by the CPI and multiply by 100.
So: New Value = ([Old Value]/[CPI])*100
My code is terrible but I can get the correct values using this:
OrdinaryTimeEarningsReal
.forEach(function (z,i) {
var CPI = z["CPI"];
d3.map(z, function(b) {return b;})
.forEach(function (c) {
if(c !== "Date" && c !== "Series" && c !== "CPI" )
OrdinaryTimeEarningsReal[i][c] = ((z[c])/(CPI))*100;
});
});
But, when I do this it is somehow also updating the original OrdinaryTimeEarnings series, such that they equal each other and the original data in OrdinaryTimeEarnings is lost.
I'm not sure whether it's the fact I'm using the bare object (while iterating within it, eek!) or that the code above is actually changing the values in the original data object (and all 4 of the series I've created after are just references to it).
Either way, I can't work it out! I've tried a fair few different syntax forms but can't work it out. Help would be greatly appreciated to achieve this.

If you indeed use this code to "duplicate" your arrays:
OrdinaryTimeEarningsReal = OrdinaryTimeEarnings;
TotalEarningsReal = TotalEarnings;
then you mentioned it right, when you said that they reference the same object. In JavaScript, arrays are mutable, and using the code above you just created 2 new variables with a reference to the existing array in the memory.
In order to deep clone your array of objects, use this method:
OrdinaryTimeEarningsReal = JSON.parse(JSON.stringify(OrdinaryTimeEarnings));
TotalEarningsReal = JSON.parse(JSON.stringify(TotalEarnings));
This will create duplicates of the array and assign them to the new variables, so that when you'll edit them, the initial arrays will remain unaffected.
Now, regarding your code, it's a bit too complex. If I understood correctly what are you trying to achieve, you could simplify it as follows:
OrdinaryTimeEarningsReal
.forEach(function (z,i) {
for (var c in z) {
if (z.hasOwnProperty(c) && c !== "Date" && c !== "Series" && c !== "CPI" )
z[c] = z[c] / z.CPI * 100;
});
});
Good luck!

If I understand correctly :
data.forEach(function(d) {
for (var key in d) {
if (key !== 'Date' && key !== 'Series' && key !== 'CPI') {
d['new' + key] = (d[key] / d.CPI) * 100;
}
}
})
console.log(data)
I have added new onto the new attributes so the new admin value is newAdmin
Implemented fiddle : https://jsfiddle.net/thatOneGuy/9ywLytjf/

Related

Generate every topological sort of a graph

Consider this input array which represents a graph:
[
{"id":1,"prev":["NaN"]},
{"id":2,"prev":["NaN"]},
{"id":3,"prev":[1]},
{"id":4,"prev":[2]},
{"id":5,"prev":[2]},
{"id":6,"prev":[3,4]},
{"id":7,"prev":[5,6]}
]
My task is to find each possible option of ordering the elements in the list.
The order of the elements depends on whether the element has a previous one or not. For example, no. 7 will always be the last since it has 2 previous elements and no followers.
I tried to implement as follows but without success:
var possibleSolutions = [];
recursiveCheck(tasks.slice(), tasks.length, []);
function recursiveCheck(array, noCalls, currentSolution) {
var solution = currentSolution;
array.forEach((task, index, object) => {
if (task.prev.length <= 1 && isNaN(task.prev[0])) {
var tmpTasks = array.slice();
solution.push(task.id);
tmpTasks.splice(index, 1);
tmpTasks.forEach(el => {
el.prev.forEach((prevEl, index, object) => {
if (prevEl == task.id) {
object.splice(index, 1)
}
})
})
noCalls--;
if (noCalls == 0) {
possibleSolutions.push(solution)
solution = [];
} else {
recursiveCheck(tmpTasks, noCalls, solution);
}
}
});
}
This should be the output:
[
[1,2,3,4,5,6,7],
[1,2,3,4,6,5,7],
[1,2,3,5,4,6,7],
[1,2,4,3,5,6,7],
[1,2,4,3,6,5,7],
[1,2,4,5,3,6,7],
[1,2,5,3,4,6,7],
[1,2,5,4,3,6,7],
[1,3,2,4,5,6,7],
[1,3,2,4,6,5,7],
[1,3,2,5,4,6,7],
[2,1,3,4,5,6,7],
[2,1,3,4,6,5,7],
[2,1,3,5,4,6,7],
[2,1,4,3,5,6,7],
[2,1,4,3,6,5,7],
[2,1,4,5,3,6,7],
[2,1,5,3,4,6,7],
[2,1,5,4,3,6,7],
[2,4,1,3,5,6,7],
[2,4,1,3,6,5,7],
[2,4,1,5,3,6,7],
[2,4,5,1,3,6,7],
[2,5,1,3,4,6,7],
[2,5,1,4,3,6,7],
[2,5,4,1,3,6,7]
]
As another example, the array cannot be arranged like [1,3,6,...] because 6 has a previous element 4 and therefore element 4 must be before 6.
Let's discuss this a bit more abstractly.
A directed graph is the data structure we're really taking as input here. We have a set V of vertices/nodes and a set E of edges. Each edge is an ordered pair (v1, v2), where v1 and v2 are both vertices, representing an arrow from v1 to v2. Here, we're representing the graph using the "adjacency list" representation.
The task is to find all the ways to topologically sort this graph.
We can describe the ways to topologically sort a graph as follows:
If we want to topologically sort the empty graph (the graph with no vertices), this is easy: the only way to do it is to output the empty sorting []. So the list of all ways to topologically sort the empty graph will be [[]].
Now, let's consider the problem of topologically sorting a non-empty graph. Consider a sequence s which is a topological sort of a non-empty graph G. We can consider the first element of s, which we will call x, and the sequence of all the rest of the elements, which we call s'.
We know that x must be a node in G, and we know that x cannot have any predecessors in G. In other words, there can be no node y such that (y, x) is an edge.
We also know that s' must be a topological sort of G'_x, which is G but with the node x (and all edges connecting to it) removed.
So to get all the topological sortings of G, we must find all sequences with initial element x and remaining elements s', such that x is an element of G with no predecessors and s' is a topological sorting of G'_x.
const remove_node = (g, x) =>
g.flatMap((node) => node["id"] == x ?
[] :
{"id": node["id"],
"prev": node["prev"].filter((id) => id != x)});
const topological_sorts = (g) =>
g.length == 0 ?
[[]] :
g.filter((node) => node["prev"].length == 0)
.map((node) => node["id"])
.flatMap((id) =>
topological_sorts(remove_node(g, id)).map((sorting) =>
[id].concat(sorting)));
const remove_nan = (g) => g.map((node) =>
({ "id" : node.id,
"prev": node.prev.filter((predecessor) =>
predecessor !== "NaN") } ));
const get_answer = (g) => topological_sorts(remove_nan(g));
Calling get_answer on your graph will result in the correct answer.

How to add a new numerical (string) key and shift the rest by +1 without breaking reactivity in Vuejs data

I have a Vue component that builds the below into a blog form field. The writer is allowed to creatively add/slot any field of choice in between each other when building a blog post ...(like: title, paragraph, blockquote, image) in an object like:
{"post":{"1":{"title":{"name":"","intro":""}},"2":{"paragraph":{"text":"","fontweight":"default-weight","bottommargin":"default-bottom-margin"}},"3":{"image":{"class":"default-image-class","creditto":""}},"4":{"subheading":{"text":"","size":"default"}}}};
I've tried using jQuery each to iterate and add it up into a makedo "dataObj" object and inject it back on the data:
data: { treeData: myUserData.post },
injectFieldType: function(type, position){
var storeObj = {};
var dataObj = this.treeData;
var crntKey;
$.each( dataObj, function( key, value ) {
if(key < position)
{
//remain same as key is not to change
}
else if(key == position)
{
dataObj[''+(parseInt(key)+1)] = dataObj[key]; /*push key further right with +1*/
dataObj[key] = /*add injected field here*/;
}
else if(key > position)
{
dataObj[''+(parseInt(key)+1)] = dataObj[key]; /*push the rest*/
}
});
and inject it back with (this.treeData = dataObj;) when it has injected the desired key and has shifted the rest by adding 1 to their keys when this is clicked:
<button type="button" v-on:click="injectFieldType('image','2')">
I need to have {"post":{"1":{"title":{"name":"","intro":""}},"2":{"image":{"class":"default-image-class","creditto":""}},"3":{"paragraph":{"text":"","fontweight":"default-weight".... When I try to inject the image field in-between the existing "name" and "paragraph" fields and make the paragraph key now 3 (instead of the old 2).
I want "{1:{foo}, 2:{bar}"} to become => {"1:{foo}, 2:{moo}, 3:{bar}" }(notice 3 changed key)
NOTE: the number order is needed to align them reliably in publishing. And data: { treeData: myUserData.post } needs to agree with the changes to allow creating the field and updating each form "name" attribute array.
There are a few problems to address here.
Firstly, trying to use var dataObj = this.treeData; and then this.treeData = dataObj isn't going to help. Both dataObj and this.treeData refer to the same object and that object has already been processed by Vue's reactivity system. You could address the reactivity problems by creating a totally new object but just creating an alias to the existing object won't help.
Instead of creating a new object I've chosen to use this.$set in my example. This isn't necessary for most of the properties, only the new one added at the end really needs it. However, it would have been unnecessarily complicated to single out that one property given the algorithm I've chosen to use.
Another potential problem is ensuring all numbers are compared as numbers and not as strings. In your example you're passing in the position as the string '2'. Operators such as < will give you the expected answer for numbers up to 9 but once the number of items in treeData reaches 10 you may start to run into problems. For string comparision '2' < '10' is false.
The next problem is the order you're moving the entries. In your current algorithm you're overwriting entry key + 1 with entry key. But that means you've lost the original value for entry key + 1. You'll end up just copying the same entry all the way to the end. There are two ways you could fix this. One would be to use a new object to hold the output (which would also help to address the reactivity problem). In my solution below I've instead chosen to iterate backwards through the keys.
new Vue({
el: '#app',
data () {
return {
newEntry: 'Yellow',
newIndex: 4,
treeData: {
1: 'Red',
2: 'Green',
3: 'Blue'
}
}
},
computed: {
treeDataLength () {
return Math.max(...Object.keys(this.treeData))
}
},
methods: {
onAddClick () {
const newIndex = Math.round(this.newIndex)
if (newIndex < 1 || newIndex > this.treeDataLength + 1) {
return
}
this.injectFieldType(this.newEntry, newIndex)
},
injectFieldType (type, position) {
const list = this.treeData
for (let index = this.treeDataLength + 1; index >= position; --index) {
if (index === position) {
this.$set(list, index, type)
} else {
this.$set(list, index, list[index - 1])
}
}
}
}
})
<script src="https://unpkg.com/vue#2.6.10/dist/vue.js"></script>
<div id="app">
<ul>
<li v-for="index in treeDataLength">
{{ index}}. {{ treeData[index] }}
</li>
</ul>
<input v-model="newEntry">
<input v-model="newIndex">
<button #click="onAddClick">Add</button>
</div>
The decision to use an object with number keys seems very strange. This would all be a lot easier if you just used an array.

Filter an array of nested objects

I have an array of nested objects and I have a user, which searches for a room
Here is an array of objects.
I would like to filter an array as soon as user types something
I tried a lot of functions, but nothing worked for me, here is the last example, which failed
search(val: any) {
// if input is clear - show everything, what we have
if (val === '') {
this.roomList = this.roomList;
} else {
//choose the object (objects) where rName = val
this.roomList = this.roomList.staticData.rName.filter(function(o) {
return Object.keys(o).some(function(k) {
return o[k].toString().toLowerCase().indexOf(val) != -1;
})
});
}
}
Could you please help or give me a hint?
You need to apply Array.filter() on roomList instead of staticData propety
this.roomList = this.roomList.filter(function (r) {
return r.staticData.rName.toLowerCase().indexOf(val.toLowerCase()) != -1
});
this.roomList = this.roomList.staticData.rName
This is a wrong starting point, just look at it. Then, rName is not an array, so you can't invoke .filter on it.
Here's how to do it :
this.roomListFiltered = this.roomList.filter(o => new RegExp(val,"i").test(o.staticData.rName) )
new RegExp(val,"i") performs a case-insensitive match.
Also, store the result of the filter in a different variable, otherwise you will lose your original list as it gets filtered out.

Sort arrays, variables or objects in Javascript

Im new to javascript but I have already made some scripts that really make a difference in my workflow. However I am now embarking on a project that forces me to sort data in a way I dont know howto do in Javascript. I will try to explain what I need to do as if my data was in excel but it isnt, I have only been able to put the data in 4 different arrays:
pagenumber[1,2,3,4,5] //only numbers
zipcode[77889,99887,33667,11122,44559] // only numbers
streetname[Hillroad, Hillroad, Baghdad Street, Hongway, Chinatown] //only letters
roadnumber[55,27,1,13,16] //only numbers
I would like to sort them like this, first by the zipcode, then by the roadname, then by the even roadnumbers descending, then by the odd roadnumbers ascending.
According to this new sorting I want to generate a new pagenumber but I want it to somehow relate to the (old) variable "pagenumber" so I can locate the old page and extract it to a new document with new pagenumbers. I am not asking you guys to write all the code for me but I need a little bit of advice to know firstly if it is possible to do which I think it is, secondly if it is right of me to put the data in four different arrays, thirdly if ther is any (ofcourse) smarter way to save the data so they relate to eachother more closely. Give me your thoughts. Also tips of where and what I should read is appreciated. Thank you all for the answers. However I want to point out that I write my code in Acrobat DC not for the web.
I suppose the items in your arrays are tied. So you should use [{},{},{},{},{}] instead of 4 arrays.
var items = [{pagenumber:1,zipcode:77889,streetname:Hillroad,roadnumber:55},{...},{...},{...},{...}]
Then sort each key-value property one-by-one, like below:
var x= [ {a:2,b:2,c:3}, {a:1,b:1,c:1}, {a:1,b:2,c:3}, {a:2,b:2,c:2} ];
x.sort(function(item1, item2){
var sort_a = item1.a-item2.a;
if (sort_a) return sort_a;
var sort_b = item1.b-item2.b;
if (sort_b) return sort_b;
var sort_c = item1.c-item2.c;
if (sort_c) return sort_c;
})
Or simplify it to be
x.sort(function(item1, item2){
return (item1.a-item2.a) || (item1.b-item2.b) || (item1.c-item2.c);
})
Given the data:
var pagenumber=[1,2,3,4,5]; //only numbers
var zipcode=[77889,99887,33667,11122,44559]; // only numbers
var streetname=['Hillroad', 'Hillroad', 'Baghdad Street', 'Hongway', 'Chinatown']; //only letters
var roadnumber=[55,27,1,13,16]; //only numbers
First, you need to make your data more easily manageable
var data = pagenumber.map(function(itemValue, index) {
return {
pagenumber:itemValue, // == pagenumber[index]
zipcode:zipcode[index],
streetname:streetname[index],
roadnumber:roadnumber[index]
};
});
Then sort it
data.sort(function(a, b) {
if (a.zipzode != b.zipcode) {
// numeric
return a.zipcode - b.zipcode;
}
if (a.streetname != b.streetname) {
// alpha
return a.streetname < b.streetname ? -1 : a.streetname > b.streetname ? 1 : 0;
}
if (a.roadnumber % 2 != b.roadnumber % 2) {
// even before odd
return b.roadnumber % 2 - a.roadnumber % 2;
}
// numeric
return a.roadnumber - b.roadnumber;
});
borrowing from another answer, that can be simplified to
data.sort(function(a, b) {
return (a.zipcode - b.zipcode) || (a.streetname < b.streetname ? -1 : a.streetname > b.streetname ? 1 : 0) || (b.roadnumber % 2 - a.roadnumber % 2) || (a.roadnumber - b.roadnumber);
});
Personally, I don't use the intermediate step when I can avoid it ... so the following is equivalent to bot the map and sort in one chained command
var sortedData = pagenumber.map(function(itemValue, index) {
return {
pagenumber:itemValue,
zipcode:zipcode[index],
streetname:streetname[index],
roadnumber:roadnumber[index]
};
}).sort(function(a, b) {
return (a.zipcode - b.zipcode) || (a.streetname < b.streetname ? -1 : a.streetname > b.streetname ? 1 : 0) || (b.roadnumber % 2 - a.roadnumber % 2) || (a.roadnumber - b.roadnumber);
});
// sorting zipcode in ascending order
zipcode.sort();
// sorting streetname in ascending order
streetname.sort();
// fetching evenroad numbers
var roadnumbereven=roadnumber.filter(function(element, index, array) {
return (element % 2 === 0);
});
// fetching odd roadnumbers
var roadnumberodd = roadnumber.filter(function(element, index, array) {
return (element % 2 !== 0);
});
// sorting even road numbers in ascending order
roadnumbereven.sort();
// sorting odd road numbers in descending order
roadnumberodd.sort(function(a,b){ return b-a; });
// merging roadnumbers(even/odd)
roadnumber = roadnumbereven.concat(roadnumberodd);
console.log(roadnumber);

Compare Objects in Array and Remove Duplicate & Update - Javascript

I have an array of objects that presents as follows:
0: Object
ConsolidatedItem_catalogId: "080808"
ConsolidatedItem_catalogItem: "undefined"
ConsolidatedItem_cost: "0"
ConsolidatedItem_description: "Test Catalog Item"
ConsolidatedItem_imageFile: "27617647008728.jpg"
ConsolidatedItem_itemNumber: "1234"
ConsolidatedItem_quantity: "1"
ConsolidatedItem_source: "CAT"
ConsolidatedItem_status: "02"
ConsolidatedItem_umCode: "EA"
1: Object
ConsolidatedItem_catalogId: ""
ConsolidatedItem_catalogItem: "undefined"
ConsolidatedItem_cost: "0"
ConsolidatedItem_description: "ALARM,SHUTDOWN SYSTEM,AXIOM,XP3, 0-1500 PSIG, HIGH AND LOW PRES Testing"
ConsolidatedItem_imageFile: ""
ConsolidatedItem_itemNumber: "10008"
ConsolidatedItem_quantity: "1"
ConsolidatedItem_source: "INV"
ConsolidatedItem_status: "02"
ConsolidatedItem_umCode: "EA"
I'm trying to update and remove an object if it's added again, or update the object. Preferably update the object with the new value. My code is as follows:
var result = $.grep(finalObject, function(e) {
return e.ConsolidatedItem_itemNumber == o.ConsolidatedItem_itemNumber;
});
console.log(result);
if (result.length == 0) {
finalObject.push(o);
shoppingCounter = finalObject.length;
$('#numberShoppedItems').text(shoppingCounter);
console.log(finalObject);
} else if (result.length == 1) {
finalObject.filter(function(x){
result = x;
console.log(result);
return x == result.ConsolidatedItem_itemNumber;
});
} else {
alert('Multiples Found');
}
}
I've tried multiple ways of getting the exact object and manipulating the data, however they've all failed. I would prefer to update the object, say if CatalogItem_itemNumber held the same value, if the CatalogItem_quantity was different - add the CatalogItem_quantity values together and update the array of objects.
I don't need an exact answer, a nudge in the right direction would do wonders though. I've looked at several of the related questions over the past couple of hours but none of them seem to address the issue. If you know of a question that has an answer, feel free to just link that as well. I may have missed it.
No Underscore.js please
When you find the matching record, you may update it by using $.extend
$.extend(result[0], o)
This will update the object in finalObject array in-place.
Alternatively, if you want to use the filter, you will need to insert the new object in the array.
finalObject = finalObject.filter(function(x) {
return x !== result[0];
});
finalObject.push(o)
Here we are allowing all the records that are not not equal to result to be returned in the resultant array that is received in finalObject. In next line, we are adding the new record.
Solved in the following manner:
1.) Verify object is not empty.
2.) Use .some() on object to iterate through it.
3.) Check if the finalObject, which is now e, has a match for the key in my temporary object I assemble, o.
4.) Update the values that need updating and return true;
Note: Originally I was going to remove the object by its index and replace it with a new object. This too can work by using .splice() and getting the index of the current object in that array you're in.
Here is the updating version:
if (o.ConsolidatedItem_quantity != '') {
var result = $.grep(finalObject, function(e) {
return e.ConsolidatedItem_itemNumber == o.ConsolidatedItem_itemNumber;
});
if (result.length == 0) {...}
else {
finalObject.some(function (e) {
if(e.ConsolidatedItem_itemNumber == o.ConsolidatedItem_itemNumber){
var a;
a = +e.ConsolidatedItem_quantity + +o.ConsolidatedItem_quantity;
e.ConsolidatedItem_quantity = a.toString();
document.getElementById(o.ConsolidatedItem_itemNumber).value=a;
return true;
};
});
}
}

Categories