How to modify a subset of data without using variables - javascript

Using functional Javascript like Underscore, Lodhash, Ramda, Immutable JS, if I have some (semi-accurate) data like this:
var data = {
people: [
{name: 'Vishwanathan Anand', age: 46},
{name: 'Garry Kasparov', age: 52},
{name: 'Magnus Carlsen', age: 25},
],
computers: [
{name: 'Deep Blue', age: 26},
{name: 'Deep Fritz', age: 21},
{name: 'Deep Thought', age: 28},
]
}
I wish to transform it to
var data = {
people: [
{name: 'Vishwanathan Anand', age: 46, rank: 0},
{name: 'Garry Kasparov', age: 52, rank: 1},
{name: 'Magnus Carlsen', age: 25, rank 2},
],
computers: [
{name: 'Deep Blue', age: 26},
{name: 'Deep Fritz', age: 21},
{name: 'Deep Thought', age: 28},
]
}
Note how only the people substructure got rank.
I know I can,
_.extend({
computers: _.map(data.people, (p, i) => {
p.rank = i;
return p;
})}, {
computers: data.computers
})
But what if I need to do this without using any variables (no more access to data!) using underscore's chain?
Something like
_.chain(data).subset('people').map((p, i) => {
p.rank = i;
return p;
})
NOTE This is a real problem and not a matter of convenience. I am working on a project that involves creating a sort of environment for functional operators and variables are not allowed.
It seems Underscore and the like operate on the entire structure (Array / List). is there any way I can ask it to operate on a substructure while preserving the rest?

This solution is a bit unpleasant but it works for this case.
_.chain(data)
.mapObject((value, key) => {
if (key==='people') {
return value.map((p,i) => _.extend(p, {rank: i}));
} else {
return value;
}
})
.value();

With Ramda you can use R.evolve to create a function, that accepts a key and an callback (cb), and maps the items of key to the required form:
const { evolve, addIndex, map } = R
const mapPart = (cb, key) => evolve({
[key]: addIndex(map)(cb)
})
const data = {"people":[{"name":"Vishwanathan Anand","age":46},{"name":"Garry Kasparov","age":52},{"name":"Magnus Carlsen","age":25}],"computers":[{"name":"Deep Blue","age":26},{"name":"Deep Fritz","age":21},{"name":"Deep Thought","age":28}]}
const result = mapPart((o, rank) => ({ ...o, rank }), 'people')(data)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>

Related

Immutable JS - converting a collection of Maps to a List

I have the following structure in the project im working on (uses immutable JS):
data :[{name: 'john', surname: 'smith', children: [{name: 'sam'}, {name: 'ben'}]},
{name: 'jane', surname: 'jones', children: [{name: 'tim'}, {name: 'amy'}]}]
the object is converted to immutable JS via fromJS().
I need a new object in the following structure:
data :[{name: 'john', surname: 'smith', children: ['sam','ben']},
{name: 'jane', surname: 'jones', children: ['tim','amy']}]
Any pointers much appreciated.
Something like that should works
data.map(d => {d.name, d.surname, children: d.children.map(child => child.name)});
The pure JS answer of Christian doesn't exactly apply to immutable.js objects, so here are a few ways to do it with immutable.js's collection API.
There are more options (e.g. reduce), feel free to check the docs; the method descriptions usually come with examples.
const raw = [{name: 'john', surname: 'smith', children: [{name: 'sam'}, {name: 'ben'}]},
{name: 'jane', surname: 'jones', children: [{name: 'tim'}, {name: 'amy'}]}];
const data = Immutable.fromJS(raw);
function run(name, operation) {
let result;
console.time(name);
for(let i=0; i < 50000; i++){
result = operation();
}
console.timeEnd(name);
console.log(result1.toJS());
}
run('simple', () => {
// simply updating objects inside a mapping
result1 = data.map(
person => person.update('children',
kidObjs => kidObjs.map(
kid => kid.get('name')
)
)
);
});
run('withMutations and setIn', () => {
// using withMutations and setIn
result2 = data.withMutations(list => {
list.forEach((val, idx) => {
list.setIn(
[idx, 'children'],
val.get('children').map(kid => kid.get('name'))
)
});
});
});
run('withMutations and update', () => {
// slightly faster by using withMutations set/update
result2 = data.withMutations(list => {
list.forEach((val, idx) => {
list.set(idx, val.update('children', kids => kids.map(kid => kid.get('name'))))
});
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/immutable/4.0.0-rc.14/immutable.min.js"></script>
I would go with map and reduce:
const data = [{name: 'john', surname: 'smith', children: [{name: 'sam'}, {name: 'ben'}]},
{name: 'jane', surname: 'jones', children: [{name: 'tim'}, {name: 'amy'}]}];
var result = data.map(person => {
return { name: person.name,
surname: person.surname,
children: person.children.reduce((acc, c) => {acc.push(c.name); return acc;}, []) }});
console.log(result)

How do I convert an array of objects into an object (dynamically) in JavaScript [duplicate]

This question already has answers here:
Convert array of objects with same property to one object with array values
(6 answers)
Closed 2 months ago.
I have an array of objects that looks like this:
arr = [
{name: "john", age: 23},
{name: "mary", age: 40},
{name: "zack", age: 17}
]
I am trying to convert it into something like this:
{
name: ["john", "mary", "zack"],
age: ['23', 40, 17]
}
i have tried the following
arr.map(item => item.name)
arr.map(item => item.age)
return {names, ages}
and it works fine but this assumes that you already know, beforehand, the keys of the objects you're converting.
I want to be able to load the object keys and corresponding array of values dynamically. Assuming i don't know that the objects in our example array have "name" and "age" as keys.
You could reduce the array and the entries of the object and collect the values in the group of the keys.
const
data = [{ name: "john", age: 23 }, { name: "mary", age: 40 }, { name: "zack", age: 17 }],
result = data.reduce((r, o) => Object.entries(o).reduce((t, [k, v]) => {
if (!t[k]) t[k] = [];
t[k].push(v);
return t;
}, r), {});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You could get the key of the first element and then map through it. With each, get its corresponded values
const arr = [
{ name: "john", age: 23, gender: "male" },
{ name: "mary", age: 40, gender: "female" },
{ name: "zack", age: 17, gender: "male" },
]
const res = Object.keys(arr[0]).reduce((acc, el) => {
const values = arr.map((item) => item[el])
return { ...acc, [el]: values }
}, {})
console.log(res)
Assuming that each object in your list has the same keys you could get the keys of the first object
const keys = Object.keys(arr[0])
and then map through the keys with your above approach
const returnObj = {}
keys.forEach(key => {
returnObj[key] = arr.map(item => item[key])
})
return returnObj
You can use Object.entries for the mapping.
var arr = [
{name: "john", age: 23},
{name: "mary", age: 40},
{name: "zack", age: 17}
];
var entries = arr.map((item) => Object.entries(item));
var result = {};
entries.forEach(entry => {
entry.forEach(item => {
if (result[item[0]] && result[item[0]].length > 0) {
result[item[0]].push(item[1]);
} else {
result[item[0]] = [item[1]];
}
});
});
console.log(result);
You can make use of Array.reduce and Object.keys.
let arr = [
{name: "john", age: 23},
{name: "mary", age: 40},
{name: "zack", age: 17}
]
const formatData = (data) => {
return data.reduce((res, obj) => {
Object.keys(obj).map(d => {
res[d] = [...(res[d] ||[]), obj[d]]
})
return res;
}, {})
}
console.log(formatData(arr))
You can do this with Ramda
import { mergeWith, concat } from “Ramda”
const mergeConcat = mergeWith(concat)
mergeConcat(arr)

ES6 Object Spread concat

I am new to object spread. I just knew object spread can be used to concat arrays . In below example i am concatenating variable a and address key. I want to know can we add address key value to each object of a array and get output as Required Output in code.
Can any one help me good reference to learn more on Object Spread.
var a = [{
'name':'jay',
age: 31
},
{
'name':'jay1',
age: 30
},
{
'name':'jay2',
age: 29
}];
var b = {...a, ...{address: 'add'}};
//b output
{name: "jay", age: 31}
{name: "jay1", age: 30}
{name: "jay2", age: 29}
address:"add"
// Required Output
{name: "jay", age: 31, address:"add"}
{name: "jay1", age: 30, address:"add"}
{name: "jay2", age: 29, address:"add"}
{ value:1, ...a, ...b, value:3 }
equals:
Object.assign({value:1}, a, b, {value:3})
In your case you need to do that for every element of your array:
const result = a.map(obj => ({...obj, address:"add"}));

change a prop in list of object in ramda.js

I have an array of object in JSON and want to change one value's properties.
for example assume I have a key field which is unique and amount, name props.
my approach is to find an object in the list with findIndex or map then remove it and make a new object and push to it. is this good way?
can recommend better approach or functions?
Lenses might be the canonical way to deal with this, although Ramda has a number of alternatives.
const people = [
{id: 1, name: 'fred', age: 28},
{id: 2, name: 'wilma', age: 25},
{id: 3, name: 'barney', age: 27},
{id: 4, name: 'betty', age: 29},
]
const personIdx = name => findIndex(propEq('name', name), people)
const ageLens = idx => lensPath([idx, 'age'])
const wLens = ageLens(personIdx('wilma'))
const newPeople = over(wLens, age => age + 1, people)
//=> [
// {id: 1, name: 'fred', age: 28},
// {id: 2, name: 'wilma', age: 26},
// {id: 3, name: 'barney', age: 27},
// {id: 4, name: 'betty', age: 29},
// ]
Note that although newPeople is a brand new object, it shares as much as it can with the existing people. For instance, newPeople[3] === people[3] //=> true.
Also note that as well as adjusting a parameter with this lens using over, we could simply fetch the value using view:
view(wLens, people) //=> 25
Or we could set it to a fixed value with set:
set(wLens, 42, people) //=> new version of `people` with wilma's age at 42
Finally, note that lenses compose. We could have also written this:
const ageLens = idx => compose(lensIndex(idx), lensProp('age')).
Lens composition can be very powerful.
You can see this in action on the Rand REPL.
Something like this maybe?
var org =
[
{name:"one",age:1}
,{name:"two",age:2}
]
;
var newArray =
org
.map(
(x,index)=>
index === 1
?Object.assign(
{}
,x
,{name:"new name"}
)
:x
)
;

Simple JavaScript Map - Using an Object as the Return

I'm studying the map function and tried to make a contrived example which I thought would work. This code works fine:
let students = [{name: 'Susan', grades: [88, 38, 28]}, {name: 'Robert', grades: [28,97, 17]}];
let newStudents = students.map((el) => el.name);
console.log(newStudents); // [ 'Susan', 'Robert' ]
But what I really wanted was the following in the map function:
let newStudents = students.map((el) => {name: el.name});
// [ undefined, undefined ]
// I assumed to get back the following: [ {name: 'Susan'}, {name: 'Robert'} ]
Why is using an object in the return portion of the map function not allowed?
You need to wrap the object in normal function parenthesis.
let newStudents = students.map((el) => ({name: el.name}));
^ ^
let students = [{name: 'Susan', grades: [88, 38, 28]}, {name: 'Robert', grades: [28,97, 17]}];
let newStudents = students.map((el) => ({name: el.name}));
console.log(newStudents);

Categories

Resources