array.sort by more than one value - javascript

I have an array variable, filled by objects.
I need to sort this array primary by array[i].target; then secondary by array[i].weaponPriority
I can sort an array by one value, but unfortunally i cant grasp how i could refine it further.
Please advice.
var ships = []
function Ship(name, target, priority){
this.name = name;
this.target = target;
this.id = ships.length;
this.weaponPriority = priority;
}
var ship = new Ship("Alpha", 10, 3);
ships.push(ship);
var ship = new Ship("Beta", 10, 1);
ships.push(ship);
var ship = new Ship("Gamma", 10, 3);
ships.push(ship);
var ship = new Ship("Delta", 15, 2);
ships.push(ship);
function log(){
for (var i = 0; i < ships.length; i++){
var shippy = ships[i];
console.log(shippy.name + " ---, targetID: " + shippy.target + ", weaponPrio: " + shippy.weaponPriority);
}
ships .sort(function(obj1, obj2){
...
});
log();

You could try something like this:
function( obj1, obj2 ){
// We check if the target values are different.
// If they are we will sort based on target
if( obj1.target !== obj2.target )
return obj1.target-obj2.target
else // The target values are the same. So we sort based on weaponPriority
return obj1.weaponPriority-obj2.weaponPriority;
}
You will pass this function to the sort.

Related

JavaScript initializing/appending/updating array of arrays

I'm new to javascript and have been researching for quite a while now, but can't figure out this syntax. When I try to push an array into another array, it pushes the elements individually instead of creating an array of arrays like I want.
What I'm trying to do:
lastTimes as array of arrays:
lastTimes = [[1, 12435235], [2,443531923], [3,4925951]]
if: IDandTime = [5, 5959393]
append IDandTime to lastTimes as an array:
lastTimes = [[1, 12435235], [2,443531923], [3,4925951], [5, 5959393]]
or if ID (IDandTime[0]) already exists, update that array's time within lastTimes:
if IDandTime = [1, 50305240]
update ID 1's time to 50305240:
lastTimes = [[1, 50305240], [2,443531923], [3,4925951], [5, 5959393]]
Would anyone mind helping me out here please? I've tried many combinations of syntaxes and can't get it right, nor have I been successful in figuring out the proper search term to find a preexisting answer. Any suggestions are greatly appreciated.
EDIT:
code:
var lastTimes = [];
var IDandTime = [1, 5935935];
lastTimes.push(IDandTime);
result:
lastTimes = [1, 5935935]
the result I want:
lastTimes = [[1, 5935935]]
EDIT2:
Ok here is the full function I am working with. I have a node.js server with an XBee serial module, and several Arduino temperature sensors with XBee serial modules. I have a handshake mechanism working, but I'm trying to achieve error checking for when nodes drop out, so their stale data is no longer used. I feel like this is really just a problem with basic 2D array syntax though.
// Open a new serial port connection
sp.on("open", function (err) {
if (err) {
return console.log('Error opening port: ', err.message);
}
console.log('open');
var nodeCount = 0;
var nodes = []; // get rid of after debugging
var lastTimes = [];
lastTimes[0] = [0,0]; // initalize as 2D array for later
// Grab data from buffer
sp.on('data', function(data) {
// Initialize time Object
var time = new Date();
// Split incoming data by newline
var buffer0 = data.split('\n');
// New node handshake initiation received
if (buffer0 == "BROADCASTING") {
nodeCount++;
var sendID = nodeCount.toString();
sp.write(sendID);
console.log("Broadcast received. Sending identifier #" + sendID);
nodes.push(nodeCount);
}
// Preconnected node data received
if ((buffer0 != "BROADCASTING") && (nodeCount > 0)) {
var receiveTime = time.getTime();
// [ID, Temp] touple
var nodeData = buffer0[0].split(" ");
console.log("NodeID: " + nodeData[0] + " Temp(F): " + nodeData[1]);
// [ID, Time] touple
var IDandTime = [];
IDandTime.push(nodeData[0]);
IDandTime.push(time.getTime());
console.log("IDandTime: " + IDandTime);
// Check for preexisting node ID
var oldNode = 0;
var nodeIndex = 0;
for (var i = 0; i < lastTimes.length; i++) {
if (lastTimes[i][0] == IDandTime[0]) {
oldNode = 1;
nodeIndex = i;
}
}
// If new node, add new node data to lastTimes (list of [ID, Time] touples)
if (oldNode == 0) {
lastTimes[lastTimes.length] = IDandTime;
console.log("lastTimes: " + lastTimes);
}
// If preexisting node, update preexisting node time
else if (oldNode == 1) {
lastTimes[i][1] = IDandTime[1];
}
}
});
});
error from my last attempt at finding the proper syntax:
lastTimes[i][1] = IDandTime[1];
^
TypeError: Cannot set property '1' of undefined
You could use findIndex to check if first number is in some of elements in array and change that element in original array or push new element to array.
var lastTimes = [[1, 12435235], [2,443531923], [3,4925951]];
function update(val) {
var i = lastTimes.findIndex(function(e) {
return e[0] == val[0];
});
if (i != -1) {
lastTimes[i][1] = val[1];
} else {
lastTimes.push(val);
}
}
update([1, 50305240])
console.log(lastTimes)
Firstly, if data is string, then calling data.split('\n') returns Array (see. String.prototype.split()), so each of your compares if (buffer0 == "BROADCASTING") are evaluated to false. You probably want to compare first line of the data, and first line is in buffer0[0], so write the conditions if (buffer0[0] == ... ).
Then you have error in code
// If preexisting node, update preexisting node time
else if (oldNode == 1) {
lastTimes[i][1] = IDandTime[1];
}
where you are using variable i from loop, but because the loop is not terminated with break keyword, the loop is always going through the whole array and after the loop is finished, variable i is setted to lastTimes.length, which points to non existing index in the array.
I would write the loop like this:
var IDandTime = [];
var nodeIndex = nodeData[0];
IDandTime.push(nodeIndex);
IDandTime.push(time.getTime());
var foundAtIndex;
for (var i = 0, l = lastTimes.length; i < l; i++) {
if (lastTimes[i][0] === nodeIndex) {
foundAtIndex = i;
break;
}
}
if (typeof foundAtIndex === 'undefined') {
lastTimes.push(IDandTime);
} else {
lastTimes[foundAtIndex] = IDandTime;
}
You could use a pure object (without prototype) instead of an array, like this:
var lastTimes = Object.create(null);
And instead of pushing a tuple, you could just set the properties of this object. This way you don't have to handle updating or appending manually, everything just work automatically, like this:
var receiveTime = time.getTime();
// [ID, Temp] touple
var nodeData = buffer0[0].split(" ");
console.log("NodeID: " + nodeData[0] + " Temp(F): " + nodeData[1]);
// [ID, Time] touple
var IDandTime = [];
IDandTime.push(nodeData[0]);
IDandTime.push(time.getTime());
console.log("IDandTime: " + IDandTime);
lastTimes[nodeData[0]] = time.getTime();
To iterate over the values:
Object.keys(lastTimes).forEach(id => {
var value = lastTimes[id];
});
And to lookup a value by id is just:
var value = lastTimes[id];

Compute value for different properties of object

Given an array of objects
function Example(x, y){
this.prop1 = x;
this.prop2 = y;
}
var exampleArray = new Array();
exampleArray.push(nex Example(0,1));
exampleArray.push(nex Example(1,3));
Now I would like to add a function which computes the average for one of the properties
function calcAvg(exampleArray, 'prop1') -> 0.5
function calcAvg(exampleArray, 'prop2') -> 2
If I don't want to use jQuery or other libraries, is there a generic way to do this?
Solution with Array.prototype.reduce method and check for valid property:
function Example(x, y) {
this.prop1 = x;
this.prop2 = y;
}
var exampleArray = new Array();
exampleArray.push(new Example(0, 1));
exampleArray.push(new Example(1, 3));
function calcAvg(arr, prop) {
if (typeof arr[0] === 'object' && !arr[0].hasOwnProperty(prop)) {
throw new Error(prop + " doesn't exist in objects within specified array!");
}
var avg = arr.reduce(function(prevObj, nextObj){
return prevObj[prop] + nextObj[prop];
});
return avg/arr.length;
}
console.log(calcAvg(exampleArray, 'prop2')); // output: 2
I think it will work ,
You need to iterate through all Example objects in the array and add the given property's value in a variable e.g. sum and then at the end divide it by total number of objects in the array to get average.
console.log(avg(exampleArray, 'prop1'));
function avg (array, propName){
var sum = 0;
array.forEach(function(exm){
sum+= exm[propName];
});
return sum / array.length;
}
You can use Array.prototype.reduce() for it.
The reduce() method applies a function against an accumulator and each value of the array (from left-to-right) to reduce it to a single value.
function Example(x, y) {
this.prop1 = x;
this.prop2 = y;
}
function calcAvg(array, key) {
return array.reduce(function (r, a) {
return r + a[key];
}, 0) / array.length;
}
var exampleArray = [new Example(0, 1), new Example(1, 3)],
avgProp1 = calcAvg(exampleArray, 'prop1'),
avgProp2 = calcAvg(exampleArray, 'prop2');
document.write(avgProp1 + '<br>');
document.write(avgProp2);
This code iterates over every value of arr, searches for property prop in every value, pushes the value of that property to an array named values and returns the sum of all the values in values divided by the number of values in it.
function calcAvg(arr,prop){
var values = [];
for(var i = 0; i<arr.length; i++){
values.push(arr[i][prop]);
}
var sum = values.reduce(function(prev,current){
return prev+current;
});
return sum/values.length;
}
Demo is here.

Associative array was sorting indexs if it return from regexp

I need to save order in array. A normal array was destroying it, so i found associative array, but with indexes from regexp was sorting records too.
My function is
var myArray = {};
var searchIndex = '';
$("#searchList").find('li').each(function( index ) {
id = $( this ).attr('id');
if(id.match(/search(:?\d+|\w+)/)){
searchIndex = id.match(/search(\d+|\w+)/)[1];
myArray[searchIndex] = "empty";
}
});
This code works well, order are saved.
myArray[id] = "empty";
http://screenshooter.net/100008827/fhbsvjm
But when i want to remove string "search" from id, by regexp, array just sorting indexes...
searchIndex = id.match(/search(\d+|\w+)/)[1];
myArray[searchIndex] = "empty";
http://screenshooter.net/100008827/gmxusyu
But order should be last->7->9->8
JavaScript does not have associative arrays. The plain objects in JavaScript are similar to associative arrays in some ways, but the order of their properties is not guaranteed.
If you want an associative array that preserves the order of items in the order they were added, it is possible to create one from scratch. Here is a fairly simple implementation that provides that functionality:
function AssociativeArray() {
var items = [];
var refs = {};
this.set = function(key, value) {
if (key in refs) {
refs[key].value = value;
} else {
var entry = { key: key, value: value };
items.push(entry);
refs[key] = entry;
}
};
this.get = function(key) {
var entry = refs[key];
return entry && entry.value;
};
this.allItems = function() {
return items.slice();
};
}
var assoc = new AssociativeArray();
assoc.set(7, "hello");
assoc.set(3, "goodbye");
assoc.set("cheer", "yay");
var items = assoc.allItems();
for (var i = 0; i < items.length; i += 1) {
console.log(items[i].key + " - " + items[i].value);
}
console.log("The item with key 7 is: " + assoc.get(7));
The way you would adapt this to your current code is:
var myArray = new AssociativeArray();
$("#searchList").find('li').each(function( index ) {
var id = $( this ).attr('id'),
searchIndex;
if(id.match(/search(:?\d+|\w+)/)){
searchIndex = id.match(/search(\d+|\w+)/)[1];
myArray.set(searchIndex, "empty");
}
});
The first code snippet above shows how to iterate through the array in the order that items were added.

JavaScript basics: array or arrays

I'm trying to learn JavaScript and am going through an exercise where I'm creating a grocery list that populates with a food, quantity, and cost. I cannot seem to pass in multiple variables or make an array of arrays. I tried some other options like "new Object" but I can't get anything off the ground. Give me a clue?
var groceryList = function(food, quantity, price) {
var theItem = [food, quantity, price]
var theList = new Array();
theList.push(theItem)
}
myList = new groceryList("cookie", 2, 1.00)
console.log(myList)
Use this
var groceryList = function(food, quantity, price) {
var theItem = [food, quantity, price]
var theList = new Array();
theList.push(theItem);
return theList;
}
myList = new groceryList("cookie", 2, 1.00)
console.log(myList)
If you want to use objects, then you need to change your thinking a little bit. When you create an object with new then the constructor gets called.
function GroceryList(food, quantity, price) {
this.food = food;
this.quantity = quantity;
this.price = price;
}
GroceryList.prototype.toString = function() {
return this.food + (this.quantity).toString() + (this.price).toString();
}
// lazy array syntax
var GroceryListPool = [];
// popular the array list pool
var list1 = new GroceryList("Butter", 2, 3.999);
GroceryListPool.push(list1);
To iterate the GroceryListPool array:
for(var i = 0; i < GroceryListPool.length; i++) {
var list = GroceryListPool[i];
// list is an object of type GroceryList
// technically it is not a "type", but you know what I mean.
alert(list);
}
That's not even really a Constructor, yet. Check this out.
function groceryList(food, quantity, price){
this.items = {};
if(food !== undefined){
this.items[food] = {quantity:quantity, price:price, total:quantity*price};
}
this.addItem = function(food, quantity, price){
this.items[food] = {quantity:quantity, price:price, total:quantity*price};
}
this.getFood(food){
return this.items[food];
}
this.getQuantity = function(food){
return this.items[food].quantity;
}
this.getTotal = function(food){
return this.items[food].total;
}
this.getItemsByPrice(low, high){
var r = {}, t = this.items;
for(var i in t){
var f = t[i], p = f.price;
if(p >= low && p <= high){
r[i] = f;
}
}
return r;
}
}
var groc = new groceryList('potato', 4, 0.89);
groc.addItem('orange', 10, 1);
console.log(groc.getQuantity('potato'));
console.log(groc.getTotal('orange'));
console.log(groc.getFood('orange').price);
// same as
console.log(groc.getPrice('orange'));
// or
console.log(groc.items.orange.price);
groc.addItem('pear', 200, 0.75);
console.log(groc.getItemsByPrice(0.25, 0.99)); // should be Object with 'potato' and 'pear'

Concatenate to access an object property [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Dynamic object property name
I want to dynamically generate access to an object's property.
If I try to access mydata[i].val.name I get somename.
If I try it like mydata[i] + bar[j] (where bar[j] === '.val.name') it fails.
How do I dynamically create something like this? So that I can access any property of an object using a user generated value?
Some code:
If I have an object I want to be able to iterate through its properties, gathering the ones I am interested in. Ideally I would like something like the following:
var processData = function (data, keys, values) {
var returnData = [], i, j, k;
var parsedData = JSON.parse(data);
var keys = keys || null;
var values = values || null;
var datalen = parsedData.length;
for (i = 0; i < datalen; i++) {
returnData[i] = {};
for(j = 0; j< keys.length; j++){
for(k = 0; k < values.length; k++){
returnData[i][keys[j]] = parsedData[i] + values;
}
}
}
return returnData;
};
and then use it like:
var keys = ["foo","bar"];
var values = [".val.name", ".val.date"];
processData(data, keys, values);
But this does not work and in console I see foo="[object Object].val.name" rather than the expected foo="ACME Industries".
If you want to stick to your pattern of constructing the subscript as a string with dots in it you have to roll your own lookup function, like so:
function descend(object, sub) {
var handle = object,
stack = sub.split('.'),
history = [],
peek;
while (handle[stack[0]]) {
if (peek) {
history.push(peek);
}
peek = stack.shift();
handle = handle[peek];
}
if (stack.length > 0) {
history.push(peek);
throw "Traversal error, could not descend to '" + stack.join('.') + "' from '" + history.join('.') + "'.";
}
return handle;
}
var x = {
a: {
b: {
c: 15
},
d: 4
}
};
console.log(descend(x, "a"));
console.log(descend(x, "a.b"));
console.log(descend(x, "a.b.c"));
console.log(descend(x, "a.d"));
function processData(data, keys, values) {
if (keys.length !== values.length) {
throw "Mismatched keys and value lookups";
}
var i,
len = keys.length,
gathered = {},
k,
scratch,
v;
for (i = 0; i < len; i += 1) {
k = descend(data, keys[i]);
scratch = values[i].split('.');
scratch.shift();
v = descend(k, scratch.join('.'));
gathered[keys[i]] = v;
}
return gathered;
}
var data = {
foo: {
val: {
name: "ACME Industries"
}
},
bar: {
val: {
date: (new Date())
}
}
};
var keys = ["foo","bar"];
var values = [".val.name", ".val.date"];
processData(data, keys, values);
Please note: this will not be nearly as performant as coding without this style of lookup.
If you try:
new Object() + '.john.doe'
It will concatenate as a string, so you’ll get "[object Object].john.doe".
You should create a function that can handle dynamic property names instead (and there are plenty of those). You also might want to loose the ".foo.bar" syntax as a string (unless you plan to use eval()) and work solely with arrays instead.
If I understand correctly you need to use
mydata[i]["val"]["name"]
So, I'd use something like this:
var result =getItemByValuesPath(myData[i],values);
alert(result);
function getItemByValuesPath(item, values)
{
var result = item;
var vals = values.split(".");
for(var j=0; j<values.length; j++)
{
if(result==undefined)
{
return null;
}
result = result[values[j]];
}
}

Categories