I have an array called 'country' which looks like:
country=[
{
"name": "china",
"id": "country:china"
}, {
"name": "city1",
"id": "country:country1>city1"
}, {
"name": "city2",
"id": "country:country1>city2"
}, {
"name": "city3",
"id": "country:country1>city3"
}, {
"name": "korea",
"id": "country:korea"
}, {
"name": "australia",
"id": "country:australia"
}
]
I am looking at rearranging/grouping the above array as:
countryResult = [ china, country1(city1, city2, city3), korea, australia]
I have written the following code but this does not give me the desired result:
$scope.countryInfo = function(itemData){
var countryResult = [];
for(var i=0; i<itemData.length; i++){
var countryItem = itemData[i];
if(countryItem.id.indexOf('>') > -1){ //Found city
var itemId = countryItem.id.substr(0, countryItem.id.indexOf('>'));
for(var j=0; j<$scope.countryData.length; j++){
if($scope.countryData[j].id == itemId){
var _parentChild = $scope.countryData[j].name + "( " + countryItem.name + " ) ";
countryResult.push(_parentChild);
}
}
}
else{
countryResult.push(countryItem.name);
}
}
return countryResult;
}
The result is coming up like this - [ china, country1(city1), country1(city2), country1(city3)), korea, australia]
Please let me know how to achieve the expected array result.
EDIT: I am just looking at simplifying the array [ china, country1(city1), country1(city2), country1(city3)), korea, australia] to [ china, country1(city1, city2, city3), korea, australia]
I've used reduce with an initial object with a keys property to capture the positions of the elements:
function sorter(countty) {
var obj = country.reduce(function (p, c, i) {
var key, id = c.id.split(/[:>]/);
if (id.length === 3) {
key = id[1];
if (!p[key]) {
p[key] = [];
p.keys.push(key);
}
p[key].push(id[2]);
} else {
p.keys.push(c.name);
}
return p;
}, { keys: [] });
return obj.keys.map(function (el) {
return obj[el] ? el + '(' + obj[el].join(', ') + ')' : el;
});
}
sorter(country);
DEMO
You can use a temporary object and then map the object's properties to the wanted array.
var country = [{ "name": "china", "id": "country:china" }, { "name": "city1", "id": "country:country1>city1" }, { "name": "city2", "id": "country:country1>city2" }, { "name": "city3", "id": "country:country1>city3" }, { "name": "korea", "id": "country:korea" }, { "name": "australia", "id": "country:australia" }],
temp = {},
result;
country.forEach(function (a) {
var b = a.id.split(/\:|\>/g);
temp[b[1]] = temp[b[1]] || [];
if (b[2]) {
temp[b[1]].push(b[2]);
}
});
result = Object.keys(temp).map(function (k) {
if (temp[k].length) {
return k + '(' + temp[k].join(', ') + ')';
}
return k;
});
document.write('<pre>' + JSON.stringify(result, 0, 4) + '</pre>');
I wrote this bit of code to change your first array (in your "EDIT") into your second array, I'm not saying it's clean, but it works:
sorts the array, then tries to figure out if the countries match, and if they have cities that need to be merged...
//an array of countries, some of which include comma delimited cities wrappedin parentheses
var arrayPlaces = ["china", "country1(city1)", "korea", "country1", "country1(city2)", "country1(city3)", "australia", "korea", "home(delicious)", "home(nacho)", "home(plate)"];
//creates an alphabatized version of the array, to simplify finding matching countries
var arrayPlacesSorted = arrayPlaces.sort();
//defines a regular expression (to search for matches countries)
var hasCity = function (sTemp) {
var re = /\(/;
if (re.test(sTemp)) {
return true;
} else {
return false;
}
};
var countryRe = /(.*?)\(.*?\)/;
var cityRe = /.*?\((.*?)\)/;
//function that loops through array, checks for matching countries, combines/adds cities
var groupCities = function (aTemp) {
var currentCountry,
currentCity,
nextCountry,
nextCity;
for (var i = 0; i < (aTemp.length); i++) {
if (hasCity(aTemp[i])) {
currentCountry = countryRe.exec(aTemp[i])[1];
currentCity = cityRe.exec(aTemp[i])[1];
} else {
currentCountry = aTemp[i];
currentCity = false;
}
if (hasCity(aTemp[i + 1])) {
nextCountry = countryRe.exec(aTemp[i + 1])[1];
nextCity = cityRe.exec(aTemp[i + 1])[1];
} else {
nextCountry = aTemp[i + 1];
nextCity = false;
}
if (currentCountry === nextCountry && nextCity && currentCity) {
aTemp[i] = currentCountry + "(" + currentCity + "," + nextCity + ")";
aTemp.splice(i + 1, 1);
i = 0;
} else if (currentCountry === nextCountry && nextCity) {
aTemp[i] = currentCountry + "(" + nextCity + ")";
aTemp.splice(i + 1, 1);
i = 0;
} else if (currentCountry === nextCountry && currentCity) {
aTemp[i] = currentCountry + "(" + currentCity + ")";
aTemp.splice(i + 1, 1);
i = 0;
} else if (currentCountry === nextCountry) {
aTemp[i] = currentCountry;
aTemp.splice(i + 1, 1);
i = 0;
}
}
return aTemp;
};
var result = groupCities(arrayPlacesSorted);
console.log(result);
Related
I have this chunk of code and I want to print this out.
a
|->b
|->c
|->e
It works when I use
let spaceStr = " ".repeat(level) + "|->";
but not when I use the helper function getSpace.
It only prints the following:
a
|->b
I cannot figure out why. Can someone explain to me?
const obj =
{ name: 'a', children:
[ { name: 'b', children: [] }
, { name: 'c', children:
[ { name: 'e', children: [] }
]
}
]
}
function getSpace(level){
var str = '';
for (i=0; i < level; i++){
str += ' ';
}
str += '|->';
return str
}
function getPath(obj, level) {
let result = [];
let resultString = '';
let spaceStr = " ".repeat(level) + "|->";
// let spaceStr = getSpace(level);
if (obj.children.length === 0) {
return spaceStr+obj.name;
} else {
if (level === 0) {
result.push(obj.name);
} else {
result.push(spaceStr + obj.name);
}
for (i=0;i<obj.children.length;i++){
result.push(getPath(obj.children[i], level+1));
}
}
return result;
}
function printPath(result){
for (i=0;i<result.length;i++){
console.log(result[i]);
}
return
}
printPath(getPath(obj,0).flat());
By using i=0 in your loops, you're not scoping the variable i correctly to the loop. Instead, it will bubble up to the closest instance of that variable name (until it hits the global scope). Consider using let i = 0 in your loops to properly scope the variable. See the following functioning code:
const obj = {
"name": "a",
"children": [{
"name": "b",
"children": []
},
{
"name": "c",
"children": [{
"name": "e",
"children": []
}]
}
]
}
function getSpace(level){
var str = '';
for (let i = 0; i < level; i++){
str += ' ';
}
str += '|->';
return str
}
function getPath(obj, level) {
let result = [];
let resultString = '';
//let spaceStr = " ".repeat(level) + "|->";
let spaceStr = getSpace(level);
if (obj.children.length === 0) {
return spaceStr+obj.name;
} else {
if (level === 0) {
result.push(obj.name);
} else {
result.push(spaceStr + obj.name);
}
for (let i = 0;i<obj.children.length;i++){
result.push(getPath(obj.children[i], level+1));
}
}
return result;
}
function printPath(result){
for (let i = 0;i<result.length;i++){
console.log(result[i]);
}
return
}
printPath(getPath(obj,0).flat());
I am practicing Javascript by creating a battleship game, and I am trying to display the user a message when it finishes the game.
if (hit && model.shipsSunk === model.numShips) {
view.displayMessage("You Sank all my battleships, in " + this.guesses + " guesses");
}
But nothing happens and when I console.log the value of shipsSunk is equal 9 instead of 3 (numShips = 3, so I need that exact number to be able to run the code above).
var view = {
displayMessage: function(msg) {
var messageArea = document.getElementById("messageArea");
messageArea.innerHTML = msg;
},
displayHit: function(location) {
var cell = document.getElementById(location);
cell.setAttribute("class", "hit");
},
displayMiss: function(location) {
var cell = document.getElementById(location);
cell.setAttribute("class", "miss");
}
};
var model = {
boardSize: 7,
numShips: 3,
shiplength: 3,
shipsSunk: 0,
ships: [
{locations: ["06", "16", "26"], hits: ["", "", ""] },
{locations: ["24", "34", "44"], hits: ["", "", ""] },
{locations: ["10", "11", "12"], hits: ["", "", ""] }],
fire: function(guess) {
for (var i = 0; i < this.numShips; i++) {
var ship = this.ships[i];
var index = ship.locations.indexOf(guess);
if (index >= 0) {
ship.hits[index] = "hit";
view.displayHit(guess);
view.displayMessage("HIT!");
if (this.isSunk(ship)) {
view.displayMessage("You sank my battleship!");
this.shipsSunk++;
}
return true;
}
}
view.displayMiss(guess);
view.displayMessage("You missed.");
return false;
},
isSunk: function(ship) {
for (var i = 0; i < this.shipLength; i++) {
if (ship.hits[i] !== "hit") {
return false;
}
}
return true;
}
};
var controller = {
guesses: 0,
processGuess: function(guess) {
var location = parseGuess(guess);
if (location) {
this.guesses++;
var hit = model.fire(location);
if (hit && model.shipsSunk === model.numShips) {
view.displayMessage("You Sank all my battleships, in " + this.guesses + " guesses");
}
}
}
};
function parseGuess(guess) {
var alphabet = ["A", "B", "C", "D", "E", "F", "G"];
if (guess === null || guess.length !== 2) {
alert("Oops, please enter a letter and a number on the board.");
} else {
firstChar = guess.charAt(0);
var row = alphabet.indexOf(firstChar);
var column = guess.charAt(1);
if (isNaN(row) || isNaN(column)) {
alert("Oops, that isn't on the board.");
} else if (row < 0 || row >= model.boardSize || column < 0 || column >= model.boardSize) {
alert("Oops, that's off the board!");
} else {
return row + column;
}
}
return null;
};
controller.processGuess("A0");
controller.processGuess("A6");
controller.processGuess("B6");
controller.processGuess("C6");
controller.processGuess("C4");
controller.processGuess("D4");
controller.processGuess("E4");
controller.processGuess("B0");
controller.processGuess("B1");
controller.processGuess("B2");
console.log(model.shipsSunk);
The question falls into category: reading the answer is followed by facepalm a.k.a. stupid mistake. The answer is simple. In your model you define shiplength: 3 with lower-case l and in your isSunk function you read the value of this.shipLength with upper-case L. Fix one or the other and the code will work as expected.
I am trying to parse the data from a .RC (resource definition file) to JSON using js with simple fs.readFile function, however I am getting SyntaxError: Invalid or unexpected token. At the moment I've found no other solution to parse this type of file, pls provide me with inputs to do so. Below is .RC example.
#include "shapes.h"
ShapesCursor CURSOR SHAPES.CUR
ShapesIcon ICON SHAPES.ICO
ShapesMenu MENU
{
POPUP "&Shape"
{
MENUITEM "&Clear", ID_CLEAR
MENUITEM "&Rectangle", ID_RECT
}
}
I found a package in node called, 'Jison' which is an API for creating parsers in JavaScript similar to 'PegJS', however I couldn't figure out the grammer that needs to be written for my file type also if it would support the file type. The parsed structure could look similar to,
{
"directive": [
"include",
"shapes.h"
],
"data": [
{
"name": "ShapesCursor",
"values": [
"CURSOR",
"SHAPES.CUR"
],
"children": []
},
{
"name": "ShapesIcon",
"values": [
"CURSOR",
"SHAPES.ICO"
],
"children": []
},
{
"name": "POPUP",
"values": [
"&Shape"
],
"children": [
{
"name": "MENUITEM",
"values": [
"&Clear",
"ID_CLEAR"
],
"children": []
},
{
"name": "MENUITEM",
"values": [
"&Rectangle",
"ID_RECT"
],
"children": []
}
]
}
]
}
Below grammar can be used for parsing Resource Description files to a JSON.
{
var word_Obj = {};
var word_alnum_Obj = {};
var word_alnumsp_Obj = {};
var dir_Obj = {};
var valStateDec_Obj = {};
var stateName_Obj = {};
var valStateVal_Obj = {};
var word_Count = 0;
var word_alnum_Count = 0;
var word_alnumsp_Count = 0;
var dir_Count = -1;
var valStateDec_Count = 0;
var stateName_Count = -1;
var valStateVal_Count = -1;
var begin_Count = -1;
var end_Count = -1;
var child_Count = -1;
var children_Count = -1;
var flag = 0;
}
Expected = dir:directive * nl * states:state nl* Ending:EOL {if (begin_Count === end_Count) { return { Parsed_RC: { Directive_Obj: dir, States_Obj: states, ENDING: Ending } } } else { return "Incorrect .RC file" } }
directive = "#" d: word space* v:word_alnumsp nl+ { dir_Count++; dir_Obj = { ['Directive_' + dir_Count]: { "Dir": d, "_text": v } }; return dir_Obj;}
state = (valState looping)*
looping = (loopStart loopEnd*)*
loopStart = beg child: valState + { begin_Count++; children_Count++; child_Count++; return {['loopStart_' + begin_Count]: begin_Count, ['child_' + children_Count]: child } }
loopEnd = end {end_Count++; child_Count--; return { ['loopEnd_' + end_Count]: end_Count }; }
valState = stateName(valStateVal) * nl +
//valStateDec = space+ decState_val:word_alnumsp {valStateDec_Count++; valStateDec_Obj = {['ValStateDec_Obj'+valStateDec_Count]:decState_val}; return valStateDec_Obj;}
stateName = space * state_name:word_alnumsp {if (state_name === 'BEGIN') { begin_Count++; children_Count++; child_Count++; return { ['loopStart_' + begin_Count]: begin_Count } } if (state_name === 'END') { end_Count++; child_Count--; return { ['loopEnd_' + end_Count]: end_Count }; } if (child_Count < 0) { flag = 1; } stateName_Count++; stateName_Obj = { ['StateName_Obj' + stateName_Count]: state_name }; return stateName_Obj; }
valStateVal = space + valState_val:(word_alnumsp comma?)* { valStateVal_Count++; valStateVal_Obj = { ['_attributes_Obj' + valStateVal_Count]: valState_val }; return valStateVal_Obj}
word = wo:letter + { return wo.join("") }
word_alnum = al:alnum + { return al.join("") }
word_alnumsp = alsp:alnumsp + { return alsp.join("") }
beg = space * ([{]) space* nl +
end = space * ([}]) space* nl +
comma = space * [,] space* { return ","}
letter = [a - zA - Z]
alnum = [a - zA - Z0 - 9]
alnumsp = [a - zA - Z0 - 9"'&._()\\|+:]
space = " "
nl = '\n' {valStateVal_Count = -1; return "NL"}
EOL = !. {return "EOF"}
Firs of all, I am newbie with Javascript and I think it is from about two hours that I am trying to understand this issue.
I have some objects (nested to one other object) where one property (called years) have this value: "1972-1974", for example. The value may change, but what remains the same is the presence of -. Object that have this property (year) with the value - need to be replicated, changing only year. If the value is "1972-1974" I want three object with value 1972, 1973 and 1974 in the year property (other property does not change).
Example of data structure.
{
"type": "FeatureCollection",
"crs": {
"type": "name",
"properties": {
"name": "urn:ogc:def:crs:OGC:1.3:CRS84"
}
},
"features": [
{
"type": "Feature",
"properties": {
"years": "1972-1974",
}
},
This is my attempt to solve my requiriment:
function formatYears(data) {
data.forEach(
function(d, index) {
if(typeof d.properties.years === "string" && d.properties.years.includes("-")) {
var temp = d.properties.years.split("-");
var count = temp[temp.length - 1] - temp[0];
d.properties.years = parseInt(temp[0]);
for(var i = 0; i < count; i++) {
var newData = jQuery.extend({}, d);
newData.properties.years = parseInt(temp[0]) + i + 1;
data.push(newData);
}
}
}
)
}
I call this function with
formatYears(json.features);
where json store all data previusly loaded.
I do not know why but all new objects have as years property 1974, in our example.
I tried to debug it and newData.properties.years take the right value (1973 and then 1974), but newData after data.push(newData) does not hold 1973 and 1974, but always 1974.
UPDATE:
Add link to json.
UPDATE 2:
Solved.
function formatYears(data) {
data.forEach(
function(d, index) {
if(typeof d.properties.years === "string" && d.properties.years.includes("-")) {
var temp = d.properties.years.split("-");
var count = temp[temp.length - 1] - temp[0];
d.properties.years = parseInt(temp[0]);
for(var i = 0; i < count; i++) {
var newData = jQuery.extend(true, {}, d);
newData.properties.years = parseInt(temp[0]) + i + 1;
data.push(newData);
}
}
}
)
}
Why?
Simple for loop:
function formatYears(features) {
features.forEach(function(feature){
var fromto = feature.properties.years.split('-');
var years = [];
for (var y = +fromto[0]; y <= fromto[1]; ++y)
years.push(y);
feature.properties.years = years;
return years;
});
}
var f = [
{
"type": "Feature",
"properties": {
"years": "1972-1974",
}
},
{
"type": "Feature",
"properties": {
"years": "1975-1978",
}
}];
function formatYears(features) {
features.forEach(function(feature){
var fromto = feature.properties.years.split('-');
var years = [];
for (var y = +fromto[0]; y <= fromto[1]; ++y)
years.push(y);
feature.properties.years = years;
});
}
formatYears(f)
console.log(f.map(f => f.properties.years))
I have made some changes to your js code
function formatYears(data) {
var out = [];
data.forEach(
function(d, index) {
if(typeof d.properties.years === "string" && d.properties.years.includes("-")) {
var temp = d.properties.years.split("-");
var count = (parseInt(temp[1],10) - parseInt(temp[0],10)) + 1;
//d.properties.years = parseInt(temp[0]);
for(var i = 0; i < count; i++) {
var newData = jQuery.extend(true, {}, d);
newData.properties.years = parseInt(temp[0], 10) + i;
console.log(newData);
out.push(newData);
}
}
}
)
console.log(out);
}
and when I am calling this with a data like this
var data = {
"features": [
{
"type": "Feature",
"properties": {
"years": "1972-1974",
}
},
{
"type": "Feature",
"properties": {
"years": "2007-2008",
}
},
{
"type": "Feature",
"properties": {
"years": "2010-2011",
}
}
]
};
its working fine.
Solved.
function formatYears(data) {
data.forEach(
function(d, index) {
if(typeof d.properties.years === "string" && d.properties.years.includes("-")) {
var temp = d.properties.years.split("-");
var count = temp[temp.length - 1] - temp[0];
d.properties.years = parseInt(temp[0]);
for(var i = 0; i < count; i++) {
var newData = jQuery.extend(true, {}, d);
newData.properties.years = parseInt(temp[0]) + i + 1;
data.push(newData);
}
}
}
)
}
So I have little bit of a tricky problem right here:
I'm getting 5x2 arrays from this function
for (var i = 0; i < 5; i++) {
var name = $('.lesson-space:eq( ' + i + ' ) > .lesson').map(function(){return $(this).data('name');}).get();
var color = $('.lesson-space:eq( ' + i + ' ) > .lesson').map(function(){return $(this).data('color');}).get();
};
For each of these 5 repeats I want to put the two arrays into an object like this
{
"name": "deutsch",
"color": "red"
},
{
"name": "mathe",
"color": "blue"
},
{
"name": "sport",
"color": "darkblue"
},
{
"name": "franz",
"color": "yellow"
}
These objects should be put now into in array. So in the end I would like to have 5 arrays (from the first code snipped) put into one array like this
[
[
...
],[
...
],[
...
],[
...
],[
...
]
]
I know it's a bit complicated...
As I understood you want to do something like this
var res = [],
data = {};
for (var i = 0; i < 5; i++) {
data = $('.lesson-space:eq( ' + i + ' ) > .lesson').map(function () {
var name = $(this).data('name');
var color = $(this).data('color');
if (!name || !color) {
return null;
}
return {
name: name,
color: color
}
}).get();
res.push(data);
}
I'm not sure I've understood you requirements correctly, however I think this is what you're looking for
var result = [];
for (var i = 0; i < 5; i++) {
result.push($('.lesson-space:eq('+i+') > .lesson').get().map(function(){
return {
name: $(this).data('name'),
color: $(this).data('name')
};
}));
}
console.log(result);
This should work
var mainArray = [];
for (i = 0; i < name.length; i++) {
var o = {};
o.name = name[i];
o.color = color[i];
mainArray.push([o])
}