I have an object like this:
I would like to do two things.
Sort the properties based on their values
I would to know the order (or index) for any given property. For example, after ordering, I would like to know that the index of 00D is the 5th.
How can I achieve this in JavaScript?
While you can not sort properties of an object, you could use an array of keys of the object, sort them and take the wanted element, you want.
var keys = Object.keys(object), // get all keys
indices = Object.create(null); // hash table for indices
// sort by value of the object
keys.sort(function (a, b) { return object[a] - object[b]; });
// create hash table with indices
keys.forEach(function (k, i) { indices[k] = i; });
// access the 5th key
console.log(keys[5]);
// get index of 00G
console.log(indices['00G']);
Related
I have an json object:
json = {
"array": [80, 50]
}
A third party jsoneditor, returns a path variable as a list of strings or index to the value. I use event listeners to detect changes to the value and I store those changes in a separate object to later submit to a form.
For example, if I change 50 to 60, this is what is returned:
node.path = ["array", 1]
node.value = 60
I am using reduceRight() to normally map the changes to the object to the changed object, however when the value is an Array it is converting the array to an object.
let delta = node.path.reduceRight((obj, elem) => ({[elem]: obj}), node.value)
//returns
delta = {array: {1: 60}}
//instead of
delta = {array: [80, 60]}
How can I check the original json and if the field is an Array object, don't reduce this into an object but keep the original Array and update the Array with the changed value within the Array? I need to submit the entire array into the form.
EDIT:
I know that I can do some custom logic within the reduce to check the original json and get the unchanged part of the array. I just dont know how to achieve that
let delta = node.path.reduceRight( function (obj, elem) {
//some logic here to account for arrays and get original array list and change only slected index
else {
return {[elem]: obj}), node.value
}
});
EDIT:
Alternatively, how can can I get the nested keys from the node.path and find the original array and then just update the array index? The path can sometimes be nested sothe path is always going to be the Array-like structure.
//this does not work
let orig_key_val = json[node.path]
Thanks!
This seems to work, finally... I used a regular reduce() and check if the key is an Array if it is store my original Array into a tmp object and save that key. The next time the reduce() comes around, if the index is on the last path element then set the tmp[tmp_key][key] to my target value and return the adjusted for Array object instead.
I can see how this will not work for nested json objects but for now I dont have any... Unless someone can show me how to figure that one out this is my implementation for now.
let tmp = {};
let tmp_key;
let delta = node.path.reduce((val, key, index, array) => {
if (Array.isArray(json[key])) {
tmp[key] = json[key]
tmp_key = key;
} else if (Object.keys(tmp).length !== 0 && index === node.path.length-1) {
tmp[tmp_key][key] = node.value;
return tmp
} else
return {[key]: val}
}, node.value);
i have an array like [x/0/2 , x/0/3 , y/3/1 , x/1/1 , x/0/3 , x/1/2],
i need to convert the elements range like [x/0/2-3 , y/3/1 , x/1/1-2]
Please give some suggestion for this.
Use reduce to iterate over the array and create an object grouped by the element root, then use Object.entries to pull out the correct information from the object.
const arr = ['x/0/2', 'x/0/3', 'y/3/1', 'x/1/1', 'x/0/3', 'x/1/2'];
const out = arr.reduce((acc, c) => {
// `split` out the separate parts of the element
const [ root1, root2, index ] = c.split('/');
// We'll use the first two parts as the object key
const key = `${root1}/${root2}`;
// If the key doesn't already exist create an empty
// array as its values
acc[key] = acc[key] || [];
// To prevent duplicates only add an index if it
// isn't already in the array
if (!acc[key].includes(index)) acc[key].push(index);
// Return the accumulator for the next iteration
return acc;
}, {});
// Then iterate over the object entries with `map`
const result = Object.entries(out).map(([ key, values ]) => {
// Return the joined up value
return `${key}/${values.join('-')}`;
});
console.log(result);
If I understand your question, you could create an array within the array to hold the range of values. Checking if the position in the array is an actual array let’s you know there are values that span a range within.
Example:
var values = [x/01, [x/20, x/21, x/22], x/03]
You could also create an object that could accomplish something similar depending on your needs.
I am new to javascript and I want to loop through json data and add accumulative number to the end of it.
Here is a sample dataset:
[{'a':123,'b':345,'c':{'b1':1,'b2':2,'b3':3}},
{'a':234,'b':456,'c':{'b1':2,'b2':4,'b3':6}},
{'a':345,'b':567,'c':{'b1':3,'b2':6,'b3':9}}]
And this is what I want to achieve:
[{'a':123,'b':345,'c':{'b1':1,'b2':2,'b3':3},'d':345+1+2+3},
{'a':234,'b':456,'c':{'b1':2,'b2':4,'b3':6},'d':456+2+4+6},
{'a':345,'b':567,'c':{'b1':3,'b2':6,'b3':9},'d':567+3+6+9}]
Use Array.map() to iterate the array, and generate new objects that contain the property d. For each object in the array, use destructuring with rest to assign a value to a property, and the rest of the properties to an object (named rest in this case).
Create a new object, using a and rest (by spreading it), and add the d property, which is the sum of all rest properties.
To sum object properties recursively I've added the sumProps method. The function uses Object.values() to get an array of values. Then it iterates an sum them using Array.reduce(). Unless a value is an object, in which case we run it through sumProps again.
const data = [
{'a':123,'b':345,'c':{'b1':1,'b2':2,'b3':3}},
{'a':234,'b':456,'c':{'b1':2,'b2':4,'b3':6}},
{'a':345,'b':567,'c':{'b1':3,'b2':6,'b3':9}}
];
const sumProps = (o) =>
Object.values(o) // get the values of an object
.reduce((r, v) => // iterate the values
r + (typeof v === 'object' ? sumProps(v) : v) // add the values to the previous sum, and if the values are an object sum it recursively
, 0);
// iterate the objects with map
const result = data.map(({ a, ...rest }) => { // destructure a, and an object of all other params
return ({ // combine them back to an object
a,
...rest, // spread the rest object
d: sumProps(rest) // get the sum of the rest, and assign to d
});
});
console.log(result);
Task: convert an array into an object with one key-value pair, where the first array item is the key, and the last array item is the value.
E.g., [1,2,3] should convert to {1: 3}
I can't get it to work as:
function transformFirstAndLast(array) {
var firstLast = {
array[0]: array[-1]
};
return firstLast
}
But only as:
function transformFirstAndLast(array) {
var firstLast = {};
firstLast[array[0]] = array[array.length - 1];
return firstLast
}
...why doesn't the first work? Why can't you index the array for the key & value?
You could pop the last element and take a computed property for the object. (For the first element, you could take Array#shift, if you like to do it in the same manner.)
function transformFirstAndLast(array) {
return { [array[0]]: array.pop() };
}
console.log(transformFirstAndLast([1, 2, 3]));
ES5 with a temporary variable.
function transformFirstAndLast(array) {
var temp = {};
temp[array[0]] = array.pop();
return temp;
}
console.log(transformFirstAndLast([1, 2, 3]));
Take the first is easy, take the last is the size minus one like this:
function firstAndLast(array) {
var ary = {};
ary[array[0]] = array[array.length - 1];
return ary;
}
console.log(firstAndLast([1,2,3]))
First, you must remember than an array is a type of JavaScript object and, in JavaScript, an object property (a.k.a. "key") can be accessed or assigned in two ways:
via "dot notation"
object.property = value;
via array syntax
object["property"] = value;
Next, remember that, in JavaScript, if you assign a value to a property that doesn't exist (using either syntax from above), the property will be created, like in the following:
console.log(window.someNewProperty); // undefined
window.someNewProperty = 17; // This causes the property to be created
window["someOtherNewProperty"] = "Voilla!"; // So does this, just with array syntax
console.log(window.someNewProperty); // 17
console.log(window["someOtherNewProperty"]); // "Voilla!"
Now, moving on to the specifics of an array, it's critical to understand the difference between an object property/key name (which is always represented as a string) and an array index (which is always a non-negative integer up to the max integer in JavaScript). So, if you have an array and seemingly assign a value to a negative index, you are actually creating a property that is named the negative index and not actually adding to the length of the array or making a new indexed position in the array. We can see that here:
var myArray = ["a", "b", "c"];
myArray[-1] = 15;
console.log(myArray.length); // 3 not 4
console.log(myArray[-1]); // 15
// Now to prove that -1 is a string name for a new property and not an index:
console.log(myArray); // Notice no 15 in the indexed values?
// And, if we enumerate the object (not just the indexes), we'll see that we actually created
// a property with [-1], not a new index.
for(var prop in myArray){
// Note that prop is not the value of the property, it's the property name itself
console.log(typeof prop, prop, myArray[prop]);
}
So, to sum up, Arrays have non-negative integer indexes to store the items that make up the length of the array, but Arrays are also objects and have properties, like all other objects do. Any bracket assignments that use anything other than non-negative integers as the key name will become new properties, not array indices.
I'm trying to sort some xml into different arrays and I'm a bit stuck on how to go about this.
Here is the xml: http://pastebin.ca/1754892
I'm using this xpath expression to get every "series" element that contains at least one "item" child node.
var ceerez = theXML.evaluate( '//series[item]' ,theXML, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null );
then I'm iterating over the result and putting the elements in a regular array:
var newArr=[];
for (var m = 0; m < ceerez.snapshotLength; m++){
newArr.push(ceerez.snapshotItem(m));
}
so what I would like to do next is to map and sort the elements in the newArr array into new arrays for each different sort.
I want to have one array that is numerically sorted by the "pub" attribute (as numbers) for each series element.
One array that is sorted alphabetically for each "title" child element's text.
One array that is sorted by date for each "item" child element.
I know how to do this with the sort() method, but I cant figure out how to do it from within the map() method so that I can make it into a new array.
var a2z1 = newArr.map(function(item){
item
.sort(function(a,b) {
return a.firstElementChild.textContent.toLowerCase().replace(/[^a-z,0-9]/gm, '') < b.firstElementChild.textContent.toLowerCase().replace(/[^a-z,0-9]/gm, '') ? -1 : 1;
});
);
(a.firstElementChild) would be the "title" element.
Make two other copies of the array (or three, if you don't want to use the original) and, using the sort method, sort each array (in place) by the criteria you specified.
Something like:
var copy1 = newArr.slice(0);
var copy2 = newArr.slice(0);
var copy3 = newArr.slice(0);
copy1.sort(function (a, b) {
// sort logic
});
copy2.sort(function (a, b) {
// sort logic
});
copy3.sort(function (a, b) {
// sort logic
});