Supposed that I have two arrays to compare:
var arrOriginal={currency1: 1234, currency2: 2345};
var arrToCompare={currency1: 123};
I was trying to find the difference between the two.
Example result:
var result={currency1: 1111, currency2: 2345}
What I've tried:
Using for .. in and then substract.
for (var key in this.arrOriginal) {
this.result.push({
currency: key,
amount: format.formatCurrency(this.arrToCompare[key] - this.arrOriginal[key])
});
}
But it won't show the currency key which the second array don't have.
Any help is appreciated.
You could take a default value of zero for undefined properties of the object.
From inside to out:
Get entries from the object as an array of key/value pairs.
Map this entries and get an object with the key and a value of the difference of value and the corresponding value of the other object or zero.
Spread the object as parameters for Object.assign.
And get a new object with all objects from mapping.
var original = { currency1: 1234, currency2: 2345 },
deltas = { currency1: 123 },
result = Object.assign(
{},
...Object.entries(original).map(([k, v]) => ({ [k]: v - (deltas[k] || 0)}))
);
console.log(result);
A very simple approach is looping over the keys of arrOriginal, If that key is present in arrToCompare then substract that value else do not substract.
var arrOriginal={currency1: 1234, currency2: 2345};
var arrToCompare={currency1: 123};
var out = {}
for(var key in arrOriginal){
out[key] = arrOriginal[key] - (arrToCompare[key] || 0)
}
console.log(out)
You can use Object.entries and reduce
var arrOriginal={currency1: 1234, currency2: 2345};
var arrToCompare={currency1: 123};
let final = Object.entries(arrOriginal).reduce((op,[key,value])=>{
op[key] = value - (arrToCompare[key] || 0)
return op
},{})
console.log(final)
A very easy to understand approach would be to have an if-else statement with three branches that check if the key exists in both or either one of the original objects:
const original = { currency1: 1234, currency2: 2345 }
const toCompare = { currency1: 123 }
const result = {}
const allKeys = new Set(Object.keys(original).concat(Object.keys(toCompare)))
allKeys.forEach(k => {
if (k in original && k in toCompare) {
result[k] = original[k] - toCompare[k]
} else if (k in original) {
result[k] = original[k]
} else {
result[k] = toCompare[k]
}
})
console.log(result)
Related
This question already has answers here:
Create an object based on file path string
(3 answers)
Closed 1 year ago.
I have an array of structured strings with have connection | as a format which self-divided into levels. I want to convert it into a structured object with multiple levels.
Input:
[
"clothes|tshirt|tshirt-for-kids",
"clothes|coat|raincoat",
"clothes|coat|leather-coat",
"clothes|tshirt|tshirt-for-men",
"clothes|tshirt|tshirt-for-men|luxury-tshirt",
]
Expected output:
{
clothes: {
tshirt: {
tshirt-for-kids: {},
tshirt-for-men: {
luxury-tshirt: {}
}
},
coat: {
raincoat: {}
leather-coat: {}
}
}
}
Very simple task - just enumerate the array and create the relevant object keys:
var myArray = [
"clothes|tshirt|tshirt-for-kids",
"clothes|coat|raincoat",
"clothes|coat|leather-coat",
"clothes|tshirt|tshirt-for-men",
"clothes|tshirt|tshirt-for-men|luxury-tshirt",
]
var result = {}, levels, current, temp;
while(myArray.length > 0)
{
levels = myArray.pop().split('|');
temp = result;
while(levels.length > 0)
{
current = levels.shift();
if(!(current in temp)) temp[current] = {};
temp = temp[current];
}
}
console.log(result);
You could try this:
const input = [
"clothes|tshirt|tshirt-for-kids",
"clothes|coat|raincoat",
"clothes|coat|leather-coat",
"clothes|tshirt|tshirt-for-men",
"clothes|tshirt|tshirt-for-men|luxury-tshirt",
];
function convertStrToObject(str, sep, obj) {
const sepIndex = str.indexOf(sep);
if (sepIndex == -1) {
obj[str] = obj[str] || {};
} else {
const key = str.substring(0, sepIndex);
obj[key] = obj[key] || {};
convertStrToObject(str.substring(sepIndex + 1), sep, obj[key]);
}
}
const all = {};
for (let i = 0; i < input.length; ++i) {
convertStrToObject(input[i], "|", all);
}
console.log(all);
Assuming you intend to collect properties, all having an empty object as leaf node.
// input
const input = [
"clothes|tshirt|tshirt-for-kids",
"clothes|coat|raincoat",
"clothes|coat|leather-coat",
"clothes|tshirt|tshirt-for-men",
"clothes|tshirt|tshirt-for-men|luxury-tshirt",
];
// Here, we collect the properties
const out = {};
// map the input array, splitting each line at |
input.map(i => i.split('|'))
.filter(a => a.length > 0) // lets not entertain empty lines in input
.forEach(a => { // process each array of property names
// start at outermost level
let p = out;
// iterate properties
for(const v of a){
// create a property if it is not already there
if(!p.hasOwnProperty(v)){
p[v] = {};
}
// move to the nested level
p = p[v];
}
});
// lets see what we have created
console.log(out);
A number of solutions have been suggested already, but I'm surprised none involves reduce() - which would seem the more idiomatic solution to me.
var array = [
"clothes|tshirt|tshirt-for-kids",
"clothes|coat|raincoat",
"clothes|coat|leather-coat",
"clothes|tshirt|tshirt-for-men",
"clothes|tshirt|tshirt-for-men|luxury-tshirt",
]
var object = array.reduce(function (object, element) {
var keys = element.split("|")
keys.reduce(function (nextNestedObject, key) {
if (!nextNestedObject[key]) nextNestedObject[key] = {}
return nextNestedObject[key]
}, object)
return object
}, {})
console.log(object)
One Liner With eval
Used eval to evaluate strings like the following:
'o["clothes"]??={}'
'o["clothes"]["tshirt"]??={}'
'o["clothes"]["tshirt"]["tshirt-for-kids"]??={}'
const
data = ["clothes|tshirt|tshirt-for-kids", "clothes|coat|raincoat", "clothes|coat|leather-coat", "clothes|tshirt|tshirt-for-men", "clothes|tshirt|tshirt-for-men|luxury-tshirt"],
arr = data.map((d) => d.split("|")),
res = arr.reduce((r, a) => (a.forEach((k, i) => eval(`r["${a.slice(0, i + 1).join('"]["')}"]??={}`)), r), {});
console.log(res)
One Liner Without eval
const
data = ["clothes|tshirt|tshirt-for-kids", "clothes|coat|raincoat", "clothes|coat|leather-coat","clothes|tshirt|tshirt-for-men", "clothes|tshirt|tshirt-for-men|luxury-tshirt"],
res = data.reduce((r, d) => (d.split("|").reduce((o, k) => (o[k] ??= {}, o[k]), r), r), {});
console.log(res)
Hey guys I'm working on a project and I'm getting an object that looks like this:
{
"userChoice[language]": "en",
"userChoice[responses][favColor]": "black",
"userChoice[responses][favCity]": "new york",
}
How do I normalize that? So I can access the properties that I need?
Thanks
Here is a es5 approach to normalize the object
function normalisedObject(object) {
var normalised = {};
for (var key in object) {
//regex to capture all [.*] groups
var matches = key.match(/\[(.*?)\]/g);
if (matches) {
var temp = normalised;
while (matches.length > 0) {
//get first key and replace []
var newKey = matches[0].replace(/\[|\]/g, "");
if (matches.length !== 1) {
//keep traverse if the left over keys are greater than 1
temp = temp[newKey] || (temp[newKey] = {}); //assign empty object
} else {
temp[newKey] = object[key];
}
//poll
matches.shift();
}
}
}
return normalised;
}
//example
const t1 = performance.now();
var object = {
"userChoice[language]": "en",
"userChoice[responses][favColor]": "black",
"userChoice[responses][favCity]": "new york"
};
var normalised = normalisedObject(object);
const t2 = performance.now();
console.log(`Operation took ${t2 - t1}ms`);
console.log(normalised);
When an object's keys don't allow you to use simple dot notation, ie obj.property, you can use square-bracket notation instead, eg
const lang = obj["userChoice[language]"]
But I have a feeling you'd actually like to transform that object into something resembling this
{
userChoice: {
language: "en",
responses: {
favColor: "black",
favCity: "new york"
}
}
}
If that's the case, you need to reduce the object entries (key / value pairs) to a new object, parsing out the key paths and building new, inner objects as you go
const obj = {
"userChoice[language]": "en",
"userChoice[responses][favColor]": "black",
"userChoice[responses][favCity]": "new york",
}
const t1 = performance.now()
const normalised = Object.entries(obj).reduce((c, [ key, val ]) => {
// get the key parts as a path array
const path = key.replace(/]/g, "").split(/\[/)
// pop the last property for assigning the value later
const prop = path.pop()
// determine the inner-most object by path
const inner = path.reduce((o, k) => {
// create a new object if it doesn't exist
return o[k] ?? (o[k] = {})
}, c)
// assign the value
inner[prop] = val
return c
}, {})
const t2 = performance.now()
console.info(normalised)
console.log(`Operation took ${t2 - t1}ms`)
.as-console-wrapper { max-height: 100% !important; }
Note that if you start throwing any array properties in, eg userChoice[foo][0], this won't work.
I have an array of object as follows:
var arr=[ {"jan":2},{"jan":5},{"feb":3},{"feb":1}];
Their will be N number of objects with any combination keys jan & feb is just an example.
I need to find average of objects with similar keys so that resultant array looks like this :
var newArr=[{"jan":3.5},{"feb":2}];
Looking to achieve this without reduce method in JavaScript.
I tried to seperate out objects with similar keys so that ic an sum and average them and push in to a new aray. something like this :
arr.forEach(a=>{
console.log(Object.keys(a))
console.log(arr.filter(ar=>ar.hasOwnProperty(Object.keys(a)[0])))
})
But it creates multiple groups for same keys like this in console.
[ {"jan":2},{"jan":5} ]
[ {"jan":2},{"jan":5} ]
[ {"feb":3},{"feb":1} ]
[ {"feb":3},{"feb":1} ]
A code without using reduce . A bit length though but easy to understand
We are using two objects , one is to store the count of the keys and other is for storing the total of the keys.
result object has the average.
var arr=[ {"jan":2},{"jan":5},{"feb":3},{"feb":1}];
var count = {};
var total = {};
arr.forEach(obj => {
var key = Object.keys(obj)[0];
if(count.hasOwnProperty(key)){
count[key]=count[key]+1;
} else {
count[key]=1;
}
if(total.hasOwnProperty(key)){
total[key]=total[key]+obj[key];
} else {
total[key]=obj[key];
}
})
var result = {}
Object.keys(total).forEach(key => {
result[key] = total[key]/count[key];
})
console.log(result)
Similar to the answer above, but makes use of a map:
var arr=[ {"jan":2},{"jan":5},{"feb":3},{"feb":1}];
let map = new Map();
let keyCount = {};
arr.forEach(e => {
let key = Object.keys(e)[0];
let value = Object.values(e)[0];
if (map.get(key) !== undefined) {
map.set(key, map.get(key) + value);
keyCount[key] = keyCount[key] + 1;
} else {
map.set(key, value);
keyCount[key] = 1;
}
});
let newArr = [];
for (let e of map.entries()) {
let obj = {};
obj[e[0]] = e[1] / keyCount[e[0]];
newArr.push(obj);
}
console.log(newArr);
I have a javascript array which get created dynamically in this format.
[{prdName: "Testing2"},
{prdName: "Testing2,Testing3"},
{markets: "Testing5"},
{markets: "Testing5,Testing6"}]
I want to remove the duplicate key in the above array map and convert it into this format.
[ {prdName: "Testing2,Testing3"},
{markets: "Testing5,Testing6"} ]
Could you let me know how to achieve the same. I am working on a reactjs application.
With ES6, you could use Map with Set for unique items.
var array = [{ prdName: "Testing2" }, { prdName: "Testing2,Testing3" }, { markets: "Testing5" }, { markets: "Testing5,Testing6" }],
map = new Map,
result;
array.forEach(o => Object.keys(o).forEach(k => {
if (!map.has(k)) {
map.set(k, new Set);
}
o[k].split(',').forEach(s => map.get(k).add(s));
}));
result = [...map].map(([k, s]) => ({ [k]: [...s].join() }));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I'm assuming that you want to preserve all of the non-duplicate comma-delimited entries, not just throw away all but the last group as in the other answers. (So, for example, if the input were [{foo: "x"}, {foo:"x,y"}, {foo:"z"}] the output should be [{foo: "x,y,z"}], not [{foo:"z"}].)
var rearrange = function(input) {
var tmp = {}; // track keys and values as we find them
for (obj of input) {
var key = Object.keys(obj)[0]; // each input has one key
tmp[key] = tmp[key] || {}; // initialize an empty object at that key in tmp if necessary
var vals = obj[key].split(",");
for (v of vals) {
tmp[key][v.trim()] = 1; // keep each of the (trimmed) comma-delimited values, implicitly dropping duplicates
}
}
// now convert the tmp object into an array:
var output = [];
for (k of Object.keys(tmp)) {
var x = {};
x[k] = Object.keys(tmp[k]).join(","); // merge the values back into a comma-separated string
output.push(x);
}
return output;
}
console.log(rearrange([
{prdName: "Testing2"},
{prdName: "Testing2,Testing3"},
{markets: "Testing5"},
{markets: "Testing5,Testing6"}
]));
console.log(rearrange([
{foo: "x"},
{foo: "x,y"},
{foo: "z"},
{bar: "x,y,z"}
]));
If, however, all you need is the last instance of each key, this is pretty close to a one-liner; just use Object.assign to merge the objects:
var rearrange = function(input) {
var merged = Object.assign({},...input); // merges all input keys, overwriting early values with later ones
// convert to array (if necessary. the "merged" object might be easier to work with...):
var output=[];
for (k of Object.keys(merged)) {
var x = {};
x[k] = merged[k];
output.push(x)
}
return output;
}
console.log(rearrange([
{prdName: "Testing2"},
{prdName: "Testing2,Testing3"},
{markets: "Testing5"},
{markets: "Testing5,Testing6"}
]));
console.log(rearrange([{foo: "x"}, {foo:"x,y"}, {foo:"z"}]));
var lists =[{prdName: "Testing2"},
{prdName: "Testing2,Testing3"},
{markets: "Testing5"},
{markets: "Testing5,Testing6"}]
var newLists =[]
var keys = []
lists.forEach(item=>{
var key = Object.keys(item)[0];
if(keys.indexOf(key) === -1){
// first time the key is processed; it is stored in newLists
keys.push(key);
newLists.push(item);
}
else {
// a duplicate key is found in the array
let remove;
let values;
newLists.forEach((item2,index) => {
if (Object.keys(item2)[0] === key) {
// use of a set to have a union of values already stored for the key and the new values found for the same key using spread operator
values = new Set([...item2[key].split(","),...item[key].split(",")]);
remove = index;
}
})
newLists.splice(remove, 1);
newLists.push({[key]: Array.from(values).toString()})
}
})
console.log(newLists);
I have several objects like this:
{'id[0]': 2}
{'url[0]': 11}
{'id[1]': 3}
{'url[1]': 14}
And I want to get something like this:
[{id:2, url:11}, {id:3, url:14}]
Also I have lodash in my project. Maybe lodash have some method for this?
You could use a regular expression for the keys and create a new object if necessary. Then assign the value to the key.
var data = [{ 'id[0]': 2 }, { 'url[0]': 11 }, { 'id[1]': 3 }, { 'url[1]': 14 }],
result = [];
data.forEach(function (a) {
Object.keys(a).forEach(function (k) {
var keys = k.match(/^([^\[]+)\[(\d+)\]$/);
if (keys.length === 3) {
result[keys[2]] = result[keys[2]] || {};
result[keys[2]][keys[1]] = a[k];
}
});
});
console.log(result);
This is an ES6 solution based on #NinaScholz solution.
I assume that the objects have only one property each, like the ones presented in the question.
Combine the array of objects to one large object using Object#assign, and convert to entries with Object.entries.
Iterate the array using Array#reduce.
Extract the original key an value from each entry using array
destructuring.
Extract the wanted key and index using a regex and array
destructuring.
Then create/update the new object at the index using object spread.
const data = [{ 'id[0]': 2 }, { 'url[0]': 11 }, { 'id[1]': 3 }, { 'url[1]': 14 }];
// combine to one object, and convert to entries
const result = Object.entries(Object.assign({}, ...data))
// extract the original key and value
.reduce((r, [k, value]) => {
// extract the key and index while ignoring the full match
const [, key, index] = k.match(/^([^\[]+)\[(\d+)\]$/);
// create/update the object at the index
r[index] = {...(r[index] || {}), [key]: value };
return r;
}, []);
console.log(result);
var arr = [{'id[0]': 2},
{'url[0]': 11},
{'id[1]': 3},
{'url[1]': 14}];
var result = [];
arr.forEach(function(e, i, a){
var index = +Object.keys(e)[0].split('[')[1].split(']')[0];//get the number inside []
result[index] = result[index] || {}; //if item is undefined make it empty object
result[index][Object.keys(e)[0].split('[')[0]] = e[Object.keys(e)[0]];//add item to object
})
console.log(result);
You can use for loop, .filter(), RegExp constructor with parameter "\["+i+"\]" where i is current index, Object.keys(), .reduce(), .replace() with RegExp /\[\d+\]/
var obj = [{
"id[0]": 2
}, {
"url[0]": 11
}, {
"id[1]": 3
}, {
"url[1]": 14
}];
var res = [];
for (var i = 0; i < obj.length / 2; i++) {
res[i] = obj.filter(function(o) {
return new RegExp("\[" + i + "\]").test(Object.keys(o))
})
.reduce(function(obj, o) {
var key = Object.keys(o).pop();
obj[key.replace(/\[\d+\]/, "")] = o[key];
return obj
}, {})
}
console.log(res);