Reduce not working as expected? - javascript

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"
}

Related

I want to remove duplicated values in side an array and get unique value

for example look at this array, I want to remove those objects which the the value of their "age" is the same.
var array =[
{age:21,name:"sam",class="C"},
{age:24,name:"david",class="f"},
{age:45,name:"zack",class="f"},
{age:21,name:"jeff",class="g"},
{age:21,name:"marco",class="a"},
{age:26,name:"john",class="d"},
];
I want to get this result:
[
{age:21,name:"sam",class="C"},
{age:24,name:"david",class="f"},
{age:45,name:"zack",class="f"},
{age:26,name:"john",class="d"},
];
You can use reduce
var array = [
{age:21,name:"sam",class:"C"},
{age:24,name:"david",class:"f"},
{age:45,name:"zack",class:"f"},
{age:21,name:"jeff",class:"g"},
{age:21,name:"marco",class:"a"},
{age:26,name:"john",class:"d"}
];
let result = array.reduce((a,v) => {
let i = a.findIndex(person => person.age === v.age);
if(i !== -1){
return a;
}
return [...a, {...v}];
},[]);
console.log(result);
You can do this
var array =[
{age:21,name:"sam",class:"C"},
{age:24,name:"david",class:"f"},
{age:45,name:"zack",class:"f"},
{age:21,name:"jeff",class:"g"},
{age:21,name:"marco",class:"a"},
{age:26,name:"john",class:"d"},
];
var res = array.filter((i, index, self) => self.findIndex(k => k.age === i.age)==index);
console.log(res);
//Another clever way with lesser complexity :)
var res = array.reduce((a,v)=>{
if(!a[v.age]){
a[v.age] = v
};
return a
},{})
console.log(Object.values(res))

JS reduce: TypeError: Cannot create property at Array.reduce

I have the following:
let keys = ['a','b','c'];
let vals = ['1','b','h'];
const my_obj = keys.reduce(function(acc,key,i) {
return acc[key]=vals[i];
},{});
logger.log(my_obj);
I would like my_obj to be {a:1,b:b,c:h}.
I'm getting:
TypeError: Cannot create property 'b' on string '1'
at Array.reduce
What am I doing wrong?
If you want to use reduce method then its fine, but you will have to return the accumulator to make it work.
let keys = ['a', 'b', 'c'];
let vals = ['1', 'b', 'h'];
const my_obj = keys.reduce(function(acc, key, i) {
acc[key] = vals[i];
return acc;
}, {});
console.log(my_obj);
But I will suggest to use the simple for loop, because of just the simplicity of problem
let keys = ['a', 'b', 'c'];
let vals = ['1', 'b', 'h'];
let my_obj = {};
for (let i = 0; i < keys.length; i++)
my_obj[keys[i]] = vals[i];
console.log(my_obj);
You need to return object as acc.
In your code you are returning the assigned value
let keys = ['a', 'b', 'c'];
let vals = ['1', 'b', 'h'];
const my_obj = keys.reduce(function(acc, key, i) {
acc[key] = vals[i];
return acc;
}, {});
console.log(my_obj);
return acc[key]=vals[i]; actually return the value of vals[i] ==> you finally get h only on https://playcode.io/ environment.
Updated: in your environment, you got that error because:
after first run of keys.reduce your next acc will be "1" (return acc[key]= vals[0]} equal return vals[0] which is "1")
Now you come to second round of keys.reduce, your acc is "1" then you set acc[key]= vals[1]} equal "1"[b] = vals[1] ==> you got that error
Try this:
let keys = ['a','b','c'];
let vals = ['1','b','h'];
const my_obj = keys.reduce(function(acc,key,i) {
// return new expanded acc
return ({...acc, [key]: vals[i]})
},{});
console.log(my_obj);

Convert string into key-value pairs in an array

I have a string:
var rrule = "DTSTART=20190514T111500Z;FREQ=DAILY;INTERVAL=1";
I want to convert this string to key-> value pairs in an array.
[
dtstart: 20190514T111500Z,
freq: daily,
interval: 1
]
I know I can take the string and split it based on the semicolon:
var array = rrule.split(";");
... but this leaves me with an array like this:
[
"DTSTART=20190514T111500Z",
"FREQ=DAILY",
"INTERVAL=1"
]
I guess I need another step to map out the keys/values, but I get lost at this point.
Ideally, for the string I want to be able to easily access what dtstarts equals, what interval equals, what other variables equal and so on.
let str = "DTSTART=20190514T111500Z;FREQ=DAILY;INTERVAL=1";
let obj = {};
for (let entry of str.split(";")) {
let pair = entry.split("=");
obj[pair[0]] = pair[1];
}
console.log(obj);
You already know how to split on the ; to get an array, from there you can just aggregate (using reduce) to get an object:
var rrule = "DTSTART=20190514T111500Z;FREQ=DAILY;INTERVAL=1";
var result = rrule.split(";").reduce( (obj,item) => {
let [key,value] = item.split("=");
obj[key] = value;
return obj;
},{});
console.log(result["DTSTART"])
console.log(result["FREQ"])
console.log(result["INTERVAL"])
You were correct to start with split first, this would then return you an array of strings.
To easily convert them, just use map, to return the split the single strings once more, and then return an object based on the property name you would like to give it and it's value
function createKeyValuePairFromString( str ) {
return str.split(';').map( item => {
const splitted = item.split('=');
return { [splitted[0]]: splitted[1] };
});
}
console.log( createKeyValuePairFromString("DTSTART=20190514T111500Z;FREQ=DAILY;INTERVAL=1") );
Use array created and split it again with =
function convertToObject(cookieString) {
const cookieObj = {};
if (!cookieString && typeof cookieString !== 'string') return cookieObj;
const arr = cookieString.split(';');
arr.forEach(record => {
if (record.includes('=')) {
const [key, value] = record.split('=');
cookieObj[key.trim()] = value;
}
});
return cookieObj;
}
You can use it like the code below:
var rrule = "DTSTART=20190514T111500Z;FREQ=DAILY;INTERVAL=1";
let finalObj = {};
rrule.split(';').forEach(i => finalObj[i.split('=')[0]] = i.split('=')[1]);
console.log('finalObj',finalObj);
Here I'm first splitting with ';' so consider the first item to be DTSTART=20190514T111500Z Then on splitting with = I get finalObject['DTSTART'] = 20190514T111500Z
Using forEach()
let str = "DTSTART=20190514T111500Z;FREQ=DAILY;INTERVAL=1";
let obj = {};
let strArr = str.split(';')
strArr.forEach((str) => {
let [key, value] = str.split('=')
obj[key] = value;
});
console.log(obj);
Here's a fairly simple version, returning an object, not an array:
const toObj = str => str
.split (';')
.map ( s => s .split ('=') )
.reduce ( (a, [k, v]) => ({...a, [k]: v}), {} )
let str = "DTSTART=20190514T111500Z;FREQ=DAILY;INTERVAL=1";
console.log (
toObj(str)
)
One of the reasons I like the library is that we can write this sort of logic more simply. In Ramda (disclaimer: I'm one of the authors), it might look like this:
const toObj = pipe ( split (';'), map (split ('=') ), fromPairs)
let str = "DTSTART=20190514T111500Z;FREQ=DAILY;INTERVAL=1";
console.log (
toObj(str)
)
<script src="https://bundle.run/ramda#0.26.1"></script><script>
const {pipe, split, map, fromPairs} = ramda; </script>
var str = "DTSTART=20190514T111500Z;FREQ=DAILY;INTERVAL=1";
// string splitting rule
const rule = (string, delimiter) => string.split(delimiter);
const result = rule(str, ';').reduce((acc, s) => {
const [key, value] = rule(s, '=');
acc[key] = value;
return acc;
}, {});
console.log(result);

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

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));

What is the most efficent way to filter an object with an array of arrays?

I'm trying to filter an Object by an array of arrays, getting back an array of objects.
Like this:
let obj =
{
"a.1":1,
"a.2":2,
"b.1":3,
"b.2":4,
"c.1":5,
"c.2":6
}
let array =
[
["a.1","b.1"],
["a"],
["b","c.1"]
]
let expectedResult =
[
{
"a.1":1,
"b.1":3,
},
{
"a.1":1,
"a.2":2,
},
{
"b.1":3,
"b.2":4,
"c.1":5
},
]
// this is what I came up with
const filterObjectByArray = (obj, arr) =>
Object.keys(obj)
.filter(ch => {
for (var index = 0; index < arr.length; index++)
if (ch.startsWith(arr[index]))
return true;
})
.reduce((ret, key) =>{
ret[key] = obj[key]
return ret
},{})
let result = array.map(arr => filterObjectByArray(obj, arr))
//kind of deepEqual
console.log(JSON.stringify(expectedResult) == JSON.stringify(result))
Is there a easier or more convenient way to do that? I need to do this operation quite often and my object will be up to couple hundreds entries big, so I see a potential bottleneck here.
I would create a one type mapping of the "base" (the letter) to the "real" keys, and then use it to translate the letter to the real keys when create the object.
const obj = {
"a.1": 1,
"a.2": 2,
"b.1": 3,
"b.2": 4,
"c.1": 5,
"c.2": 6
};
const array = [
["a.1", "b.1"],
["a"],
["b", "c.1"]
];
const getBaseKey = (key) => key.match(/^[a-z]+/)[0]; // get the base of the key - the letter. If it's only one letter, you can use key[0]
/** create a one time map of keys by their base **/
const oobjKeysMap = Object.keys(obj).reduce((map, key) => {
const baseKey = getBaseKey(key);
const curr = map.get(baseKey) || [];
curr.push(key);
return map.set(baseKey, curr);
}, new Map());
const result = array.map((sub) => // iterate the array
[].concat(...sub.map((k) => k in obj ? k : oobjKeysMap.get(getBaseKey(k)))) // create the least of "real" keys
.reduce((ret, key) => { // create the object
ret[key] = obj[key];
return ret;
}, {})
);
console.log(result);

Categories