Merge 2 arrays of objects setting key from one and value from the other - javascript

I have 2 objets a and b defined as the following :
a = {
1:3,
2:5,
3:1,
}
b = {
1:{name:"Bob"},
2:{name:"John"},
3:{name:"Alice"}
}
What I am trying to get is the following object c defined as
c = {
"Bob":3,
"John":5,
"Alice":1
}
So creating an using b[key].name as c[key] and a[key] as value.
What I tried so far is
const mapAandB = (a, b) => {
let finalObject = [];
Object.keys(b).forEach(key => {
return finalOdds.push({ [b[key].name]: a[key] });
});
return finalOdds;
};
but then the result is
c = [
0:{Bob:3},
1:{John: 5},
2:{Alice:1}
]
If you have any suggestion ...

You can use Array#reduce to collect the names and values into an object:
const a = {"1":3,"2":5,"3":1}
const b = {"1":{"name":"Bob"},"2":{"name":"John"},"3":{"name":"Alice"}}
const result = Object.keys(a).reduce((r, key) => {
r[b[key].name] = a[key];
return r;
}, {});
console.log(result);
Or you can use Array#map to create a series of objects, and combine them to one using Object#assign and spread:
const a = {"1":3,"2":5,"3":1}
const b = {"1":{"name":"Bob"},"2":{"name":"John"},"3":{"name":"Alice"}}
const result = Object.assign(...Object.keys(a).map((key) => ({ [b[key].name]: a[key] })));
console.log(result);

Try this solution. If you want to get an object instead of array, just add the result into the Object.assign(...result)
const a = {
1:3,
2:5,
3:1,
}
const b = {
1:{name:"Bob"},
2:{name:"John"},
3:{name:"Alice"}
}
const mapAandB = (a, b) => Object.keys(a).map(key => ({[b[key].name]: a[key]}));
console.log(mapAandB(a,b));

Related

how to convert string array of objects to array of objects

I have an array that I want to convert but I have no idea how to do it
how can i convert this array
const a = ['{/run:true}', '{/sleep:false}'];
in this array
const b = [{'/run':true}, {'/sleep':false}];
Using map and a regular expression:
const a = ['{/run:true}', '{/sleep:false}'];
const b = a.map(s => {
const [_,k,v] = s.match(/\{(.+):(.+)}/);
return {[k]: JSON.parse(v)};
});
console.log(b);
Or other way is to run a sting replacement and JSON.parse
const a = ['{/run:true}', '{/sleep:false}'];
const b = JSON.parse("[" + a.toString().replace(/\{([^:]+)/g, '{"$1"') + "]");
console.log(b);
Created this simple function to do exactly that
const a = ['{/run:true}', '{/sleep:false}'];
// desired output
const b = [{ run: true }, { sleep: false }];
const c = a.map(item => {
const key = item.match(/\w+/)[0];
const value = item.match(/true|false/)[0];
return { [key]: value === 'true' };
});
console.log(c);
const a = ['{/run:true}', '{/sleep:false}'];
const output = a.map(item => {
const normalize = item.replaceAll("{", "").replaceAll("}", "")
const splitedStr = normalize.split(':');
const key = splitedStr[0];
const value = splitedStr[1];
return {[key]: value}
})
console.log(output)
const a = ['{/run:true}', '{/sleep:false}'];
const c = a.map(item => {
return {
['/' + item.split(':')[1].split('{')[0]]: item.split(':')[1].split('}')[0]
};
});
console.log(c);

Looping data from json using Array

I'm trying to write a function but I doesn't make it. This function works like that
Input: changeSetting("a>b>c","hello")
After that "setting" named value change from {} to {"a":{"b":{"c":"hello"}}}
If input is changeSetting("a","hello") json become {} to {"a":"hello"}
My last code attempt:
function changeSetting(name,val) {
if (name.includes(">")) {
name = name.split('>')
let json = {}
name.map((el,i)=>{
let last = ""
name.filter(el=>!name.slice(i+1).includes(el)).map(el=> {
if(last!="") {
json[el] = {}
}})
})
}
}
How can we make this ? (Optimization not important but if is it good for me)
const changeSetting = (setting, target) => {
if (setting.length < 2) {
return {
[setting]: target
}
} else {
const keys = setting.split('>');
return keys.reduceRight((acc, curr, i) => {
console.log(acc);
if(i === keys.length - 1) {
return acc = {[curr] : target}
}
return acc = { [curr]: acc };
}, {})
}
}
console.log(changeSetting('a', 'hello'));
console.log(changeSetting('a>b>c', 'hello'));
function changeSetting(inputProperties, value) {
let result;
const properties = inputProperties.split(">");
result = `{${properties
.map((property) => `"${property}":`)
.join("{")}"${value}"${"}".repeat(properties.length)}`;
return result;
}
changeSetting("a>b>c", "hello");
changeSetting("a", "hello");
As you work with strings - you may try to use JSON like this:
function changeSetting(name, val) {
const keys = name.split(">");
return JSON.parse(
[
"{",
keys.map((key) => `"${key}"`).join(":{"),
":",
`"${val}"`,
"}".repeat(keys.length),
].join("")
);
}
There's multiple ways to do this, I've commented the snippet
const changeSetting = (name, val) => {
// Split and reverse the name letters
const nameSplit = name.split('>').reverse();
// Set up the inner most object
let newObj = {[nameSplit[0]]:val}
// Now remove the first letter and recurse through the rest
nameSplit.slice(1).forEach((el, idx) => newObj = {[el]: newObj});
console.log(newObj);
}
changeSetting("a>b>c", "hello")
changeSetting("a", "hello")
changeSetting("a>b>c>d>e>f>g", "hello")
You can create an array by splitting name on all > with String.prototype.split(), and then Array.prototype.reduceRight() the created array of elements with an object initial value {} and adding key value pairs but on the last element the value should be variable val.
Code:
const changeSetting = (name, val) => name
.split('>')
.reduceRight((a, c, i, arr) => ({
[c]: i === arr.length - 1 ? val : a
}), {})
console.log(changeSetting('a>b>c', 'hello'))
console.log(changeSetting('a', 'hello'))
console.log(changeSetting('a>b>c>d>e>f>g', 'hello'))

Filter object by value in typescript or javascript

I have an object JobAffiliation and wanted to filter it out by JobAffiliationEnum
ex. I want JobAffiliation separately for JobAffiliationEnum.AdminPortal and JobAffiliationEnum.EmployeePortal
enum JobAffiliationEnum {
AdminPortal = 1,
EmployeePortal = 2
}
enum JobTypeEnum {
Job1 = 1,
Job2 = 2,
Job3 = 3
}
const JobAffiliation = {
[JobTypeEnum.Job1] : JobAffiliationEnum.AdminPortal,
[JobTypeEnum.Job2] : JobAffiliationEnum.EmployeePortal,
[JobTypeEnum.Job3] : JobAffiliationEnum.AdminPortal | JobAffiliationEnum.EmployeePortal,
...
}
Thanks
Supposing that JobAffiliation is a valid JSON like:
const JobAffiliation = {
"JobTypeEnum.Job1": "JobAffiliationEnum.AdminPortal",
"JobTypeEnum.Job2": "JobAffiliationEnum.EmployeePortal",
"JobTypeEnum.Job3": "JobAffiliationEnum.AdminPortal | JobAffiliationEnum.EmployeePortal",
...
}
You could write something like:
let AdminPortalArray = [];
let EmployeePortalArray = [];
Object.entries(JobAffiliation).forEach(job => {
if(job[1].includes("JobAffiliationEnum.AdminPortal")) AdminPortalArray.push(job);
else if (job[1].includes("JobAffiliationEnum.EmployeePortal")) EmployeePortalArray.push(job);
});
console.log(AdminPortalArray);
// this will show [{"JobTypeEnum.Job1": "JobAffiliationEnum.AdminPortal"}, {"JobTypeEnum.Job3": "JobAffiliationEnum.AdminPortal | JobAffiliationEnum.EmployeePortal"}]
console.log(EmployeePortalArray);
// this will show [{"JobTypeEnum.Job2": "JobAffiliationEnum.EmployeePortal"}, {"JobTypeEnum.Job3": "JobAffiliationEnum.AdminPortal | JobAffiliationEnum.EmployeePortal"}]
You can use Object.entries to enlist key value pairs and then filter them out by value and then use Object.fromEntries to build the new object from that entries :
enum JobAffiliationEnum {
AdminPortal = 1,
EmployeePortal = 2
}
enum JobTypeEnum {
Job1 = 1,
Job2 = 2,
Job3 = 3
}
const JobAffiliation = {
[JobTypeEnum.Job1] : JobAffiliationEnum.AdminPortal,
[JobTypeEnum.Job2] : JobAffiliationEnum.EmployeePortal,
[JobTypeEnum.Job3] : JobAffiliationEnum.AdminPortal | JobAffiliationEnum.EmployeePortal
}
const filterValues = [JobAffiliationEnum.AdminPortal,JobAffiliationEnum.EmployeePortal];
const newObject = Object.fromEntries( Object.entries(JobAffiliation).filter(([k,v])=> filterValues.some( (f : number)=> (v & f) === f)))
console.log(newObject);
TS Playground
This (v & f) === f tests the value v has the flag f. And beware that flag values have to be the power of 2.
First we can use Object.keys to get an array of JobAffiliation object keys second filter them by equality to one of JobAffiliationEnum value and finally build a new object with filtered keys with reduce.
const EmployeePortalKeys = Object.keys(JobAffiliation) as unknown as JobTypeEnum[]
const EmployeePortal = EmployeePortalKeys.filter((key)=> JobAffiliation[key] === JobAffiliationEnum.EmployeePortal)
.reduce((obj, key) => {
obj[key] = JobAffiliation[key];
return obj;
}, {} as JobTypeEnum[])
const AdminPortalKeys = Object.keys(JobAffiliation) as unknown as JobTypeEnum[]
const AdminPortal = AdminPortalKeys.filter((key)=> JobAffiliation[key] === JobAffiliationEnum.AdminPortal)
.reduce((obj, key) => {
obj[key] = JobAffiliation[key];
return obj;
}, {} as JobTypeEnum[])

JSON array count in JavaScript

I have the array below:
[{"__metadata":{"id":"bba6f593-167d-4f14-85cf-3b69288f5434","etag":"\"1\"","type":"SP.Data.PollLogListItem"},"PollId":1,"Answer":"Option3"},{"__metadata":{"id":"925dceaf-250f-43c1-be73-9972b0a34750","etag":"\"2\"","type":"SP.Data.PollLogListItem"},"PollId":1,"Answer":"Option4"},{"__metadata":{"id":"85c73abb-a565-4e2c-b74c-1883c15e2eb6","etag":"\"1\"","type":"SP.Data.PollLogListItem"},"PollId":1,"Answer":"Option3"}]
How can I get a count by group as below:
[
{"Answer":"Option3","Count":2},
{"Answer":"Option4","Count":1}
]
I my previous array there is no Count key. I tried below but it is giving an error. Can somebody help me?
const data = [{"__metadata":{"id":"bba6f593-167d-4f14-85cf-3b69288f5434","etag":"\"1\"","type":"SP.Data.PollLogListItem"},"PollId":1,"Answer":"Option3"},{"__metadata":{"id":"925dceaf-250f-43c1-be73-9972b0a34750","etag":"\"2\"","type":"SP.Data.PollLogListItem"},"PollId":1,"Answer":"Option4"},{"__metadata":{"id":"85c73abb-a565-4e2c-b74c-1883c15e2eb6","etag":"\"1\"","type":"SP.Data.PollLogListItem"},"PollId":1,"Answer":"Option3"}];
function getCount(pollLog) {
counts = pollLog.reduce(function(r, o) {
if (r[o.Answer]) { //!(o.Answer in r)
r.push(r[o.Answer] = o);
r[o.Answer].Count = 1;
} else {
r[o.Answer].Count += 1;
}
}, {})
return counts;
}
getCount(data)
I'd use a Map for this (or an object created with Object.create(null) if you have to support obsolete environments without a polyfill for Map):
const answers = new Map();
for (const {Answer: answer} of data) {
if (answers.has(answer)) {
answers.set(answer, answers.get(answer) + 1);
} else {
answers.set(answer, 1);
}
}
Then if you really want that array of objects instead of the Map:
const array = [...answers.entries()].map(([Answer, Count]) => ({Answer, Count}));
Live Example:
const data = [{"__metadata":{"id":"bba6f593-167d-4f14-85cf-3b69288f5434","etag":"\"1\"","type":"SP.Data.PollLogListItem"},"PollId":1,"Answer":"Option3"},{"__metadata":{"id":"925dceaf-250f-43c1-be73-9972b0a34750","etag":"\"2\"","type":"SP.Data.PollLogListItem"},"PollId":1,"Answer":"Option4"},{"__metadata":{"id":"85c73abb-a565-4e2c-b74c-1883c15e2eb6","etag":"\"1\"","type":"SP.Data.PollLogListItem"},"PollId":1,"Answer":"Option3"}];
const answers = new Map();
for (const {Answer: answer} of data) {
if (answers.has(answer)) {
answers.set(answer, answers.get(answer) + 1);
} else {
answers.set(answer, 1);
}
}
console.log(...answers);
// If you want the array of objects:
const array = [...answers.entries()].map(([Answer, Count]) => ({Answer, Count}));
console.log(array);
.as-console-wrapper {
max-height: 100% !important;
}
Your reduce() logic isn't right and you aren't returning the accumulator.
The accumulator is an object so you can't push() to it. When r[o.Answer] doesn't exist you want to create a new object for that property value.
Finally instead of returning the reduce object , return it's values as array
function getCount(pollLog) {
var counts = pollLog.reduce(function(r, o) {
if (!r[o.Answer]) {
r[o.Answer] = {Answer: o.Answer, Count:0}
}
r[o.Answer].Count ++;
// must return the accumulator
return r;
}, {})
return Object.values(counts);
}
console.log(getCount(data))
<script>
var data = [{"__metadata":{"id":"bba6f593-167d-4f14-85cf-3b69288f5434","etag":"\"1\"","type":"SP.Data.PollLogListItem"},"PollId":1,"Answer":"Option3"},{"__metadata":{"id":"925dceaf-250f-43c1-be73-9972b0a34750","etag":"\"2\"","type":"SP.Data.PollLogListItem"},"PollId":1,"Answer":"Option4"},{"__metadata":{"id":"85c73abb-a565-4e2c-b74c-1883c15e2eb6","etag":"\"1\"","type":"SP.Data.PollLogListItem"},"PollId":1,"Answer":"Option3"}]
</script>
Here is an ES2017+ way to get the counts for all your array items in O(N):
const arr = [...yourArray];
const counts = {};
arr.forEach((el) => {
const answer = el['Answer']
counts[answer] = counts[answer] ? (counts[answer] += 1) : 1;
});
const countsSorted = Object.entries(counts).sort(([_, a], [__, b]) => a - b);
const result = countsSorted.map([a, c] => ({Answer: a, Count: c}))
console.log(result) for your example array:
[ { Answer: 'Option3', Count: 2 }, { Answer: 'Option4', Count: 1 } ]
Your reduce callback function is incorrect, if you need to use this code in browser or in some es5 environment, then here is one of solutions.
var data = [{"__metadata":{"id":"bba6f593-167d-4f14-85cf-3b69288f5434","etag":"\"1\"","type":"SP.Data.PollLogListItem"},"PollId":1,"Answer":"Option3"},{"__metadata":{"id":"925dceaf-250f-43c1-be73-9972b0a34750","etag":"\"2\"","type":"SP.Data.PollLogListItem"},"PollId":1,"Answer":"Option4"},{"__metadata":{"id":"85c73abb-a565-4e2c-b74c-1883c15e2eb6","etag":"\"1\"","type":"SP.Data.PollLogListItem"},"PollId":1,"Answer":"Option3"}];
function getCount(pollLog){
const answersCount = pollLog.reduce(function (r, o) {
var answer = o["Answer"];
if (!r[answer]) {
r[answer] = 1
} else {
r[answer] += 1;
}
return r;
}, {});
return Object.keys(answersCount).map(function(key) {
return {"Answer":key, "Count": answersCount[key]+""}
});
}
alert(JSON.stringify(getCount(data)));
Or here is a jsfiddle https://jsfiddle.net/hgen4Lzp/
If you can use ecma2017 (no need for IE support), go with the solution provided by #charlietfl

Reduce not working as expected?

I'm currently trying to convert an array into an object with the array index as the property of the created object.
Example Array: ['a','b','c']
Expected Object result: {'1':'a','2':'b','3':'c'}
My code is below, it worked when I used map method but when I use the reduce method instead it comes out weird way:
let sampleData = ['a','b','c'];
let convertArrToObjWithIndexProp = (arr) => {
/*let res = {};
arr.map((v,k)=> {
res[k+1]=v;
})
return res;*/
//--> this outputs {'1':'a','2':'b','3':'c'}
return arr.reduce((iv,cv,i)=>{
return Object.assign(iv,iv[i+1]=cv);
},{});
//--> this outputs instead {'0':'c','1':'a','2':'b','3':'c'}
}
console.log(convertArrToObjWithIndexProp(sampleData));
Can someone explain to me why its coming out like that?
Also is using reduce better than using map?
The problem is that result of this expression: iv[i+1]=cv is cv, which you then Object.assign to the accumulator. You could make it simpler with a simple assignment:
let sampleData = ['a','b','c'];
let convertArrToObjWithIndexProp = (arr) =>
arr.reduce((iv,cv,i) => (iv[i+1] = cv, iv),{});
console.log(convertArrToObjWithIndexProp(sampleData));
Don't use Object.assign. Just update your object and return it.
let sampleData = ['a','b','c'];
let convertArrToObjWithIndexProp = (arr) => {
return arr.reduce((iv,cv,i)=>{
iv[i+1]=cv
return iv
},{});
}
console.log(convertArrToObjWithIndexProp(sampleData));
var arr = ['a','b','c'];
var result = arr.reduce((obj, val, index) => {
obj[index + 1] = val;
return obj;
}, {});
console.log(result);
I'd do it with Array.reduce function, and Object computed property.
var sampleData = ['a', 'b', 'c'];
console.log(sampleData.reduce((mem, curr, index) => ({ ...mem,
[index + 1]: curr
}), {}))
You can achieve it by doing this
let array = ['a','b','c'];
return array.reduce((acc, currentValue, index) => {
const key= index + 1;
acc[key] = currentValue;
return acc;
}, {});
Output will be like this
{
"1": "a",
"2": "b",
"3": "c"
}

Categories