Edit parameter in variable with increment/decrement box and save it - javascript

I have a var K with several parameters A, B, L, M. I'm trying to put an increment/decrement box to "A" so it can be changed to a larger or smaller number.
If this is possible, it should remain changed (saved?) for the next time or session it is loaded.
{ L: 34, M: 56, A: 64, B: 65 },
{ L: 35, M: 55, A: 47, B: 89 },
{ L: 36, M: 54, A: 85, B: 62 },
{ L: 37, M: 53, A: 23, B: 11 }, ];

Since the question is tagged with reactjs, I assume this array of objects being in a component state, so we have to do it without mutating the original array.
Safest way to do it is by using a map() method from Array.prototype.
Example:
const K = [{ L: 34, M: 56, A: 64, B: 65 }, { L: 35, M: 55, A: 47, B: 89 }];
const newArray = K.map(element => ({...element, A: element.A + 1}));
console.log(newArray);
//[{ L: 34, M: 56, A: 65, B: 65 }, { L: 35, M: 55, A: 48, B: 89 }]
And now explanation. Map is a method that takes a function as an argument, where first parameter is actual value in each index of an array and returns a new value in it's place.
What's done here in ({...element, A: element.A + 1}), is that the object got populated with the properties from "element", then property A got overwritten with value of element.A + 1. After that it got returned as a new value. It happened the same for every single key.
The end result is a new array with each A increased by 1 and assigned to newArray.
And if it has to stay in a session, just keep it in a sessionStorage.
Useful links:
https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map

Related

Get array of object property names from array of objects

I'm looking for something similar to this:
const object1 = {
a: 1,
b: 2,
c: 3
};
console.log(Object.getOwnPropertyNames(object1));
// expected output: Array ["a", "b", "c"]
However in my example I have:
const objectArray1 = [
{ a: 112, b: 322, c: 233 },
{ a: 611, b: 232, c: 331 },
{ a: 132, b: 232, c: 342 }
];
What's the most efficient way of getting the ["a", "b", "c"] from this?
Also, that'll probably never happen but if one of objectArray1 objects has d: 345 in it or a missing key/value pair, that needs to be handled.
Any ideas?
Map all entries of objectArray1 to the object's keys using Object.keys and flatMap, to get a flat array of all keys in all objects.
Then pass it through a Set to extract the unique values. The Set can be converted back to an array using the ... spread operator:
const objectArray1 = [
{ a: 112, b: 322, c: 233 },
{ a: 611, b: 232, c: 331 },
{ a: 132, b: 232, c: 342 },
{ a: 132, b: 232, c: 342, d: 532 }
];
const keys = [
...new Set(objectArray1.flatMap(Object.keys))
];
console.log(keys)
You should use Object.keys in this case
const object1 = {
a: 1,
b: 2,
c: 3
};
const result = Object.keys(object1)
console.log(result)
If you have an array, you also can add Set to filter unique keys
const objectArray1 = [
{ a: 112, b: 322, c: 233 },
{ a: 611, b: 232, c: 331 },
{ a: 132, b: 232, c: 342 }
];
const result = Array.from(new Set(objectArray1.flatMap((value) => Object.keys(value))))
console.log(result)
I would personally use flatMap() with Object.keys(), and then make a new unique array from it using new Set() with spread operator ... .
const objectArray1 = [
{ a: 112, c: 233 },
{ b: 232, c: 331 },
{ a: 132, b: 232, c: 342, d: 5 }
]
const uniqueKeys = (arr) => [...new Set(arr.flatMap(Object.keys))]
console.log(uniqueKeys(objectArray1))
//expected ["a", "b", "c", "d"]

Combining the content of multiple objects that have the same key with that key as the property in JavaScript

I want to combine the content of multiple objects that have the same key with that key as the property in JavaScript.
Objects:
const cat1 = { med1: { a: 10, b: 12 }, med2: { c: 14, d: 16 } };
const cat2 = { med1: { e: 18, f: 20 }, med2: { g: 22, h: 24 } };
Expected output:
{
med1: { a: 10, b: 12, e: 18, f: 20 },
med2: { c: 14, d: 16, g: 22, h: 24 }
}
I have tried to use both object spreading and Object.assign with no sucess.
With object spreading, since objects have no iterator this is returning an error. Object.assign would work however since the two objects to combine have the same key, the second object is overwriting the first.
You can iterate over the object keys and create a new object out of their aggregation.
You can use ES6 spread operator (...) which allows us to quickly copy all or part of an existing array or object into another array or object.
const cat1 = { med1: { a: 10, b: 12 }, med2: { c: 14, d: 16 } };
const cat2 = { med1: { e: 18, f: 20 }, med2: { g: 22, h: 24 } };
let resultObject = {};
Object.keys(cat1).map(key => { // iterate over the keys
resultObject = {
...resultObject,
[key]: {...cat1[key], ...cat2[key]} // merge two objects
}
return;
});
console.log(resultObject);
For combining two objects with a single level of indirection 👇
const cat1 = { med1: { a: 10, b: 12 }, med2: { c: 14, d: 16 } }
const cat2 = { med1: { e: 18, f: 20 }, med2: { g: 22, h: 24 } }
const merge = (o1, o2) =>
Object.keys(o1)
.reduce((p, key) =>
(p[key] = { ...o1[key], ...o2[key] }, p), {})
console.log(merge(cat1, cat2))

How to group by multiple keys at the same time using D3?

This works, but I was wondering if there was a better way than creating a string with a and b and later splitting it:
const data = [
{ a: 10, b: 20, c: 30, d: 40 },
{ a: 10, b: 20, c: 31, d: 41 },
{ a: 12, b: 22, c: 32, d: 42 }
];
d3.rollups(
data,
x => ({
c: x.map(d => d.c),
d: x.map(d => d.d)
}),
d => `${d.a} ${d.b}`
)
.map(([key, values]) => {
const [a, b] = key.split(' ');
return {a, b, ...values};
});
// OUTPUT
// [
// {a: "10", b: "20", c: [30, 31], d: [40, 41]},
// {a: "12", b: "22", c: [32], d: [42]}
// ]
With d3 v7 released, there is now a better way to do this using the new d3.flatRollup.
const data = [
{ a: 10, b: 20, c: 30, d: 40 },
{ a: 10, b: 20, c: 31, d: 41 },
{ a: 12, b: 22, c: 32, d: 42 }
];
const result = d3.flatRollup(
data,
x => ({
c: x.map(d => d.c),
d: x.map(d => d.d)
}),
d => d.a,
d => d.b
);
console.log(result);
const flattened = result.map(([a, b, values]) => ({a, b, ...values}));
console.log(flattened);
<script src="https://cdn.jsdelivr.net/npm/d3-array#3.0.2/dist/d3-array.min.js"></script>
As you already know d3.rollups() will create nested arrays if you have more than one key:
If more than one key is specified, a nested Map [or array] is returned.
Therefore, as d3.rollups doesn't fit your needs, I believe it's easier to create a plain JavaScript function (I'm aware of "using D3" in your title, but even in a D3 code nothing forbids us of writing plain JS solutions where D3 has none).
In the following example I'm purposefully writing a verbose function (with comments) so each part of it is clear, avoiding more complex features which could make it substantially short (but more cryptic). In this function I'm using reduce, so the data array is looped only once. myKeys is the array of keys you'll use to rollup.
Here is the function and the comments:
function groupedRollup(myArray, myKeys) {
return myArray.reduce((a, c) => {
//Find the object in the acc with all 'myKeys' equivalent to the current
const foundObject = a.find(e => myKeys.every(f => e[f] === c[f]));
//if found, push the value for each key which is not in 'myKeys'
if (foundObject) {
for (let key in foundObject) {
if (!keys.includes(key)) foundObject[key].push(c[key]);
};
//if not found, push the current object with all non 'myKeys' keys as arrays
} else {
const copiedObject = Object.assign({}, c);//avoids mutation
for (let key in copiedObject) {
if (!keys.includes(key)) copiedObject[key] = [copiedObject[key]];
};
a.push(copiedObject);
};
return a;
}, [])
};
Here is the demo:
const data = [{
a: 10,
b: 20,
c: 30,
d: 40
},
{
a: 10,
b: 20,
c: 31,
d: 41
},
{
a: 12,
b: 22,
c: 32,
d: 42
}
];
const keys = ["a", "b"];
console.log(groupedRollup(data, keys))
function groupedRollup(myArray, myKeys) {
return myArray.reduce((a, c) => {
const foundObject = a.find(e => myKeys.every(f => e[f] === c[f]));
if (foundObject) {
for (let key in foundObject) {
if (!keys.includes(key)) foundObject[key].push(c[key]);
};
} else {
const copiedObject = Object.assign({}, c);
for (let key in copiedObject) {
if (!keys.includes(key)) copiedObject[key] = [copiedObject[key]];
};
a.push(copiedObject);
};
return a;
}, [])
};
And here is a demo with a more complex data:
const data = [{
a: 10,
b: 20,
c: 30,
d: 40,
e: 5,
f: 19
},
{
a: 10,
b: 55,
c: 37,
d: 40,
e: 5,
f: 19
},
{
a: 10,
b: 20,
c: 31,
d: 48,
e: 5,
f: 18
},
{
a: 80,
b: 20,
c: 31,
d: 48,
e: 5,
f: 18
},
{
a: 1,
b: 2,
c: 3,
d: 8,
e: 5,
f: 9
},
{
a: 10,
b: 88,
c: 44,
d: 33,
e: 5,
f: 19
}
];
const keys = ["a", "e", "f"];
console.log(groupedRollup(data, keys))
function groupedRollup(myArray, myKeys) {
return myArray.reduce((a, c) => {
const foundObject = a.find(e => myKeys.every(f => e[f] === c[f]));
if (foundObject) {
for (let key in foundObject) {
if (!keys.includes(key)) foundObject[key].push(c[key]);
};
} else {
const copiedObject = Object.assign({}, c);
for (let key in copiedObject) {
if (!keys.includes(key)) copiedObject[key] = [copiedObject[key]];
};
a.push(copiedObject);
};
return a;
}, [])
};
Finally, pay attention that this function will push duplicated values (in the above example d: [40, 40, 33]). If that's not what you want then just check for duplicates.
The approach below allows you to remove the split, but does not prevent the need to create a string for the compound key. In this case, using JSON.stringify({a: d.a, b: d.b}) instead of ${d.a} ${d.b}, allows for the map to return an object where the c and d properties can be assigned to the parse of the key.
This preserves some of the 'd3-ishness' of your question and the utility of rollups to deal with the creation of the arrays for c and d.
const data = [
{ a: 10, b: 20, c: 30, d: 40 },
{ a: 10, b: 20, c: 31, d: 41 },
{ a: 12, b: 22, c: 32, d: 42 }
];
const groups = d3.rollups(
data,
x => ({
c: x.map(d => d.c),
d: x.map(d => d.d)
}),
d => JSON.stringify({a: d.a, b: d.b}) // compare with `${d.a} ${d.b}`
).map(arr => Object.assign(JSON.parse(arr[0]), arr[1]));
console.log(groups);
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.5.0/d3.min.js"></script>
The approach can accommodate the extensibility of #Gerado Furtado's answer, but I fear it's getting a little hectic:
const data = [
{a: 10, b: 20, c: 30, d: 40, e: 5, f: 19},
{a: 10, b: 55, c: 37, d: 40, e: 5, f: 19},
{a: 10, b: 20, c: 31, d: 48, e: 5, f: 18},
{a: 80, b: 20, c: 31, d: 48, e: 5, f: 18},
{a: 1, b: 2, c: 3, d: 8, e: 5, f: 9},
{a: 10, b: 88, c: 44, d: 33, e: 5, f: 19}
];
const keys = ["a", "e", "f"];
const groupedRollup = (data, keys) => {
const others = Object.keys(data[0])
.filter(k => !keys.includes(k)); // finds b, c, d as not part of compound key
return d3.rollups(
data,
x => Object.assign(
{},
...others.map(k => {
return {[k]: x.map(d => d[k])} // dynamically create reducer
})
),
d => JSON.stringify(
Object.assign(
{},
...keys.map(k => {
return {[k]: d[k]} // dynamically add keys
})
)
) // and stringify for compound key
).map(arr => Object.fromEntries( // sorting the output object
Object.entries( // keys in alpha order
Object.assign(JSON.parse(arr[0]), arr[1])).sort() // same approach
)
);
}
console.log(groupedRollup(data, keys));
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.5.0/d3.min.js"></script>
There's some interesting talk about the introduction of use of InternMap in rollups and the associated functions - but I don't see either that it's ready, or that it's useful for what you are trying to do.

How do i change object properties to have double quotes?

How do I change an object to have double quotes as a property in JavaScript?
Example:
{ C: 10, H: 16, N: 5, O: 13, P: 3 }​​​​ => ​​​​​​​​​​{ "C": 10, "H": 16, "N": 5, "O": 13, "P": 3 }
You can stringify it.
var obj = { C: 10, H: 16, N: 5, O: 13, P: 3 },
json = JSON.parse(JSON.stringify(obj));
console.log(json);
I think this is what Niputi expected: double quotes as a property.
let input = {C: 10, H: 16, N: 5, O: 13, P: 3};
let output = {};
for (let key in input) {
output['"' + key + '"'] = input[key];
}
console.log(output);
Note, the original object's properties remain unchanged in the preceding two solutions in which each creates a new object. The OP indicated a desire to alter the original object. In that case, either of the preceding solutions is fine in conjunction with deleting the unquoted properties, too. One way to accomplish this feat in JavaScript is as follows:
var o = {
C: 10,
H: 16,
N: 5,
O: 13,
P: 3
};
for (let k in o) {
o["\"" + k + "\""] = o[k];
delete o[k];
}
// the changed object
for (let p in o) {
console.log(p, o[p]);
}
See live code

JavaScript: Array of Objects - Sorting by Attribute Value

I have an array of objects. I don't know how many objects there will be until the code is being run (it is returned from an API), but let's assume this is the array:
var arr = [ { A: 40, B: 88, C: 11 },
{ A: 10, B: 98, C: 65 }, // sum of A = 188
{ A: 11, B: 15, C: 18 }, // sum of B = 310
{ A: 16, B: 55, C: 16 }, // sum of C = 136
{ A: 22, B: 23, C: 13 },
{ A: 89, B: 31, C: 13 } ]
I want to look over every object in the array. I'd like the end-result to be a list of keys and values, sorted in descending order. So, if we were to use the array above, the code would return something like this:
[["B", 310], ["A", 188], ["C", 136]]
I hope it's not too much to ask if you can add comments in your code, as sometimes the answers here can be very short and efficient (and work brilliantly) but difficult to understand for a beginner with algorithms :)
Many thanks in advance.
EDIT: Each object does not necessarily always have three keys, it is around 30-40 keys.
BIT MORE INFO: I'm writing this for a stacked bar chart where I want to then only extract the top 10 keys and bucket the rest of the values into an "Others" key, but this is irrelevant to the question and only here for information.
If you are about to get sorted result -like you mentioned in your terms- for your complete array then this may be the answer.
You can first calculate sum of corresponding properties of every object in the array with a simple Array.prototype.reduce then convert the result to the structure you want (I preferred to loop over object keys) and then sort your structured array.
var arr = [ { A: 40, B: 88, C: 11 },
{ A: 10, B: 98, C: 65 },
{ A: 11, B: 15, C: 18 },
{ A: 16, B: 55, C: 16 },
{ A: 22, B: 23, C: 13 },
{ A: 89, B: 31, C: 13 }
];
var sum = arr.reduce((p, c) => {
var result = Object.create(null);
Object.keys(p).forEach(k => result[k] = p[k] + c[k]);
return result;
});
var sorted = Object.keys(sum).map(k => [k, sum[k]]).sort((a, b) => a[1] - b[1]);
console.log(sorted);

Categories