Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results. See also: Stack Overflow question checklist
Closed 9 years ago.
Improve this question
i got a table with td's that look like this:
Country | City | Number
Thailand | Phuket | 25
Thailand | Bangkok | 12
China | Wenzhou | 1
Australia | Sydney | 2
Vietnam | Saigon | 4
China | Beijing | 3
USA | New York | 4
USA | Miami | 2
How can i get a total for each country, ie Thailand = 37 (25+12), via jQuery (or plain JS)?
It would be simple if I could define each country however these values comes from a database and it can be over 50 countries/cities so need to put each in an array first and then somehow use inArray and add Number to already existing array if it exists.
Updated with jsfiddle: http://bit.ly/1iizLAt
JavaScript objects are essentially key/value maps, so you can track the total for the countries even though you don't know in advance what their names are:
To create the map:
var countryCounts = {};
I assume you can use the jQuery API to loop through the rows of the table, and get the country name for the row; let's say you put it in the variable country. Then, to count a time you've seen the country (in the loop):
countryCounts[country] = (countryCounts[country] || 0) + 1;
(Note that this assumes there's no chance that a country will have the name of any of the properties that exist by default on Object.prototype, such as toString or valueOf.)
That line looks a bit tricky, in two ways, so here's how it works:
You can look up a property name on an object either using dot notation and a property name literal, e.g., obj.foo, or by using bracketed notation and a string, e.g. obj["foo"]. In the latter case, the string can be the result of any expression. So if country is "Thailand", countryCounts[country] looks up the property Thailand on countryCounts.
If you look up a propery that doesn't exist on the object, you get back undefined, which is a "falsey" value. JavaScript's curiously-powerful || operator returns the first non-falsey (truthy) argument you give it. So countryCounts[country] || 0 will give you countryCounts[country] if that value is truthy, or 0 if countryCounts[country] is falsey. So you end up with the current count for the country, or the number 0. Then we add one to it and store it.
Then to loop through the results in countryCounts:
for (country in countryCounts) {
if (countryCounts.hasOwnPropery(country)) {
// Use `country` and `countryCounts[country]`
}
for-in loops through the names of the enumerable properties of an object. Using hasOwnProperty is a bit paranoid here, but it's good practice; it weeds out any enumerable properties that might exist on the object's prototype. (There are no enumerable properties on Object.prototype, and if you catch someone adding any you should give them a Severe Talking To, but still it's good practice.)
In the comments below, ajax333221 suggests "normalizing" the country name a bit just in case you have both Thailand and thailand (which would be separate properties in JavaScript, since JavaScript is case-sensitive). It's a good idea. At the very least, I would make the name all-lower-case, and his/her suggestion is to remove spaces as well, which is probably a good idea and unlikely to create confusion. If you want the names for display purposes, you could store the first one you see (without making it lower case and removing spaces) in a separate map:
realCountry = /*...get the real country name...*/;
country = country.toLowerCase().replace(/ /g, '_');
if (!mungedNameMap.hasOwnProperty(country)) {
mungedNameMap[country] = realCountry;
}
Then looking up the "munged" name in mungedNameMap will give you the first un-munged version you saw.
I'd first get the data in a format that you can play with in js:
var data = [
{country: 'Thailand', city: 'Asia', number: 25},
{country: 'Thailand', city: 'Bangkok', number: 12},
{country: 'China', city: 'Wenzhou', number: 1},
{country: 'Australia', city: 'Sydney', number: 2},
{country: 'Vietnam', city: 'Saigon', number: 4},
{country: 'China', city: 'Beijing', number: 3},
{country: 'USA', city: 'New', number: 4},
{country: 'USA', city: 'Miami', number: 2}
]
Once you have that, you can sum the number for matching countries:
var counts = {};
for (var i=0; i < data.length; i++){
var row = data[i];
counts[row.country] = (counts[row.country] || 0) + 1;
}
or, if you wanted a more functional approach, you could use Array.reduce
var counts = data.reduce(function(memo, row){
memo[row.country] = (memo[row.country] || 0) + 1;
return memo;
},{});
Related
I have a question on what's the best way to achieve this,
I have an array with 8 objects:
myArray = [ {id: 1, name, xxx, town: Town}, ...]
then I have a second array with another 8 objects of type Town:
townArray = [ {id: 1, name: RngTown}, ... ]
I subscribe to the first array, then I subscribe to the second, and what I want to achieve is a new array that fills the property Town of myArray, with the 8objects in townArray, order doesn't matter, it's just dummy data.
is map() the right way?
and in this example I have 8 objects in the first array, and 8 in the second, so each property Town will have a unique Town from the second array, but what would happen if I only had 6 town object? Can I map all of them to my 8objects, and when they finish after 6 towns, they start from the first town again until all objects in the first array have a town?
thank you
I did an example here.
I think this will solve your issue!
What it does is assign the towns by the id, so the result 1 will have the town 1 and so on!
stackblitz
let me know if you really want to use map.
Check in the console log the result!
I have a service producing objects that are like triples. They will be in this format:
{ country, attribute, value }
Example:
{ country: 'usa', attribute: 'population', value: 100 }
{ country: 'mexico', attribute: 'population', value: 200 }
{ country: 'usa', attribute: 'areaInSqM', value: 3000 }
Ultimately I want to display these as a table. Rows are countries, columns are attributes. So the table would look like:
| country | population | areaInSqM |
| usa | 100 | 3000 |
| mexico | 200 | |
My assumption (possibly wrong) is that I need to create an intermediate data structure that is an array of rows. Such as:
[ { country: 'usa', population: 100, areaInSqM: 3000 }, .... ]
My current solution is a non-RxJS mess of objects where I store a Set containing each attribute type, store a lookup object indexed by country, and convert the lookup object back to the above array at the end. Lots of looping and double storage that I'd prefer to avoid.
Does RxJS have any operators that aid in this type of operation?
Is there a smarter approach?
In this particular case, assumptions are:
The attributes are not known ahead of time
The values are always numeric
A given 'cell' can be null. In this example, mexico areaInSqM is never provided
Edit: Plunkr with solution: https://plnkr.co/edit/FVoeVmmzMN7JGJ3zWFQM?p=preview
There are two components in your question, the data structure part, and the data flow part (I suppose you get these data as a stream i.e. one by one, hence the reason why you use Rxjs).
A simple way to iteratively build you data structure is to use the scan operator. For instance :
myDataStructure$ = dataSource$.scan(function (accDataStructure, triple){
accDataStructure[triple.country] = accDataStructure[triple.country] || {}
accDataStructure[triple.country][triple.attribute] = accDataStructure[triple.country][triple.attribute] || {}
accDataStructure[triple.country][triple.attribute] = triple.value
return accDataStructure
}, {})
That makes the assumption that dataSource$ produces objects of the shape { country, attribute, value }. Then myDataStructure$ will output, for every incoming data, the iteratively built data structure that you are seeking. If you only want that data structure once it is finished building, just add a .last() to myDataStructure$.
This is not tested so let me know if that worked
I am working on foreach in javascript, and I am wondering how to replace its old value to the new value in an array.
Let say I have this array
var myArray = [];
and then I have this variables to store the value
var name = [{name: "Peter"}, {name:"Jimmy"}, {name: Bob}];
var country = [{country: "Canada"}, {country:"Mexico"}, {country: "United States"}];
After I push the array, and display it to the views, I should have something like this in the table:
Name: Country:
Peter Canada
Jimmy Mexico
Bob United States
Then I also have the edit button so that I can edit the content in the table
Name: Country:
Peter Canada
Jimmy Mexico
Bob United States
<button ng-click="editDetail()">Edit Button </button>
What I want to do is, I want to change to be able to replace the old content to new content in the table and save it back to the array.
Name: Country:
Amy Britian
Jimmy Spain
Ken United States
How can I use foreach methods to do it? I am not sure what to do in this foreach function. Help will be appreciated :)
myArray.foreach(function(){
})
What you'll want to do is not use forEach, but use map, which essentially is the same, but you'll return a new array with your new data replacing the old data.
See the Mozilla Docs
var myNewArray = myArray.map(function(d) {
return d;
});
I'm refining my ui.bootstrap typeahead adding some awesomness, but I'm struggling on this one.
I've created a small Plunker to demonstrate my issue:
http://plnkr.co/edit/u2Le37?p=preview
Two words about the issue:
I have this array of objects which populate my $scope.data but I'm not able to tell typeahead to use only a particular field to search the result
$scope.data = [
{
name: 'Mario',
desc: 'Super Plumber',
id: 0,
type: 'good'
},
{
name: 'Luigi',
desc: 'Assistant Plumber',
id: 1,
type: 'good'
},
...
Whenever you search in the typeahead, you'll search for every field in the object, even type and id
I've tried, without success, solution like these:
typeahead="datum.name as ( datum.name +', '+ datum.desc)
for datum in data | filter:$viewValue | limitTo:8"
---> no change
typeahead="datum as ( datum.name +', '+ datum.desc)
for datum.name in data | filter:$viewValue | limitTo:8"
--> no match
How can I restrict the search to, let's say, the name field?
The magical line is as following
filter:{name: $viewValue}
This would limit the search to only name field. Look at this document ion for ng-filter
Object: A pattern object can be used to filter specific properties on
objects contained by array. For example {name:"M", phone:"1"}
predicate will return an array of items which have property name
containing "M" and property phone containing "1". A special property
name $ can be used (as in {$:"text"}) to accept a match against any
property of the object. That's equivalent to the simple substring
match with a string as described above.
Plunker Updated
Currently, I am creating a 3d array in js using the following:
var arr = [["name1", "place1", "data1"],
["name2", "place2", "data2"],
["name3", "place3", "data3"]];
I can access each element using arr[0] or arr[1]. But is there anyways I can access them using a key like this: arr["name1"] should give me the first one. Any suggestions? I think I am looking for a Hashmap like functionality.
The situation has changed in the six years since this question was asked.
Due to weak typing associative arrays can be faked in JavaScript:
>> var names = new Array();
undefined
>> names["first"] = "Dotan";
"Dotan"
>> names["last"] = "Cohen";
"Cohen"
>> for ( key in names ) { console.log(key+" "+names[key]) }
undefined
first Dotan
last Cohen
That is sometimes useful, and all browsers released since 2012 support it, but there are caveats! The array cannot be simply read back:
>> names
Array [ ]
More importantly, the array's length cannot be easily retrieved:
>> names.length
0
Therefore this is not an associative array in the sense that JavaScript would have supported it had it been intended, but rather a workaround that is often useful if for whatever reason a real JS object does not support what you need:
>> var names = {};
undefined
>> names.first = "Dotan";
"Dotan"
>> names.last = "Cohen";
"Cohen"
>> for ( key in names ) { console.log(key+" "+names[key]) }
undefined
first Dotan
last Cohen
>> names
Object { first: "Dotan", last: "Cohen" }
>> Object.keys(names).length
2
The only way you could do that is by wrapping it in an object.
var arr = {
name1 : ["name1", "place1", "data1"],
name2 : ["name2", "place2", "data2"],
name3 : ["name3", "place3", "data3"]
};
Javascript is a prototype based dynamic language. You can create objects and change their structure when you want.
var o = {name1: {place1: data1}, name2: {place2: data2}};
and access it with:
o.name1
The look-up implementation varies though and when you have a lot of properties that often changes this can be pretty slow (except in Chrome that uses a special scheme to access object properties i.e. embedded classes a-la-dynamic dispatch from smalltalk). Some libraries (e.g. MooTools) provide some hash map related structures,