Pushing element into array inside for loop - javascript

I want to dynamically generate a 50 element array from a single element, by just modifying one value on each loop.
const eventRecords = { Records: [] }
for (let i = 0; i < 50; i++) {
const aRecord = Object.assign({}, eventS3Chunk.Records[0])
aRecord.s3.object.key = `F7kdfh2Dj3j2s8/uploads/10000_users_without_password-20190102T030405Z/${i}.csv`
eventRecords.Records.push(Object.assign({}, aRecord))
}
eventRecords.Records end up with 50 copies of same element with s3.object.key = F7kdfh2Dj3j2s8/uploads/10000_users_without_password-20190102T030405Z/49.csv.

it's because you're creating a shallow copy of Records[0], use JSON.parse(JSON.stringify(eventS3Chunk.Records[0]));
:
const eventS3Chunk = {
Records: [{
s3: {
object: {
key: "a"
}
}
}]
};
const eventRecords = Array.from({
length: 50
}, (_, i) => {
const aRecord = JSON.parse(JSON.stringify(eventS3Chunk.Records[0]));
aRecord.s3.object.key = `F7kdfh2Dj3j2s8/uploads/10000_users_without_password-20190102T030405Z/${i}.csv`;
return aRecord;
});
console.log(eventRecords)

Related

Reverse object hierarchy

is there any way to reverse the object in js?
wanna make a function, but struggling hard.
I tried to find the object depth-first and then make a founded amount of iterations for .. in .. inside the object but don't know how to rewrite the new one
const initObject = {
value: 5,
next: {
value: 10,
next: {
value: 15
next: null
}
},
}
//expected result
const newObject = {
value: 15,
next: {
value: 10,
next: {
value: 5,
next: null
}
}
}
You could use a recursive function to collect all the values. Then use reduce to create a nested object from the values:
const initObject = {
value: 5,
next: {
value: 10,
next: {
value: 15,
next: null
}
}
}
const getValues = ({ value, next }) =>
next
? [value, ...getValues(next)]
: [value]
const createObject = values =>
values.reduce((next, value) => ({ value, next }), null)
const output = createObject(getValues(initObject))
console.log(output)
let newObject = null;
for(let o = initObject; o; o = o.next) {
newObject = { value: o.value, next: newObject };
}
What this is doing is looping the initialObject from the outer layer inwards (using the next property to go from layer to layer until we reach null) while constructing the newObject from the inside outwards (each constructed layer will use the previous layer, stored as the current value of newObject, as the next property, hence the initial value of newObject is null), like so:
Note: As pointed out by #jperl in a comment bellow, if the objects have multiple properties (not just value), then simply use a spread syntax to include them all in newObject by replacing:
newObject = { value: o.value, next: newObject };
with:
newObject = { ...o, next: newObject };
Demo:
const initObject = { value: 5, next: { value: 10, next: { value: 15, next: null } } };
let newObject = null;
for(let o = initObject; o; o = o.next) {
newObject = { value: o.value, next: newObject };
}
console.log(newObject);
Here's a function which does the job, the process is:
Build an array of the value values
Reverse the array (using Array.reverse)
Rebuild your object structure with the reversed array values
const reverseObject = (object) => {
const values = [];
let currentObject = object;
while (currentObject && currentObject.value) {
values.push(currentObject.value);
currentObject = currentObject.next;
}
const reversedValues = values.reverse();
const newObject = {};
let currentNewObject = newObject;
for (let i = 0; i < reversedValues.length; i++) {
currentNewObject.value = reversedValues[i];
currentNewObject.next = null;
if (i < reversedValues.length - 1) {
currentNewObject = currentNewObject.next = {};
}
}
return newObject;
};
As Bergi already mentioned in the comments, this can be done using recursion. It is even very concise and readable if you do it as follows.
It iterates through each (nested) node and builds up an inverted object using the nextAccumulator variable.
function invert({value, next}, nextAccumulator = null) {
const node = {value, next: nextAccumulator};
return next ? invert(next, node) : node;
}
const inverted = invert({
value: 5,
next: {
value: 10,
next: {
value: 15,
next: null,
},
},
});
console.log(inverted);
You could take a single function approach with a second paramter for collecting the objects.
const
reverse = ({ value, next: sub }, next = null) => sub
? reverse(sub, { value, next })
: { value, next },
object = { value: 5, next: { value: 10, next: { value: 15, next: null } } },
result = reverse(object);
console.log(result);
You can do that in following steps:
Create a function to get all the values in form of array in order
reverse the array
convert that array back to object using recursion
const newObject = {
value: 15,
next: {
value: 10,
next: {
value: 5,
next: null
}
}
}
function reverse(obj){
let values = [];
values.reverse();
function getValue(tempObj){
values.push(tempObj.value);
if(tempObj.next){
getValue(tempObj.next);
}
}
getValue(obj)
const ob = {};
let i = 0;
function reassignValue(tempObj){
if(i >= values.length){
console.log(i)
return;
}
tempObj.value = values[i];
tempObj.next = {};
i++;
reassignValue(tempObj.next);
}
reassignValue(ob);
return ob
}
console.log(reverse(newObject))
That's a linked list. You'll easily find a reverse LinkedList algorithm on the Internet.
Note that I don't need to convert anything.
Typescript
type LinkedList = {
value: number,
next: LinkedList
} | null
const list: LinkedList = {
value: 5,
next: {
value: 10,
next: {
value: 15,
next: null
}
},
}
const reverseLinkedList = (list: LinkedList) => {
let prev = null;
let current = list
let next = null
while (current != null) {
next = current.next
current.next = prev
prev = current
current = next
}
list = prev
return list
}
console.log(reverseLinkedList(list))
Javascript
const list = {
value: 5,
next: {
value: 10,
next: {
value: 15,
next: null
}
},
}
const reverseLinkedList = (list) => {
let prev = null;
let current = list
let next = null
while (current != null) {
next = current.next
current.next = prev
prev = current
current = next
}
list = prev
return list
}
console.log(reverseLinkedList(list))
You can use recursion:
let initObject={value:5,next:{value:10,next:{value:15,next:null}}};
let flip = (obj,nexts=[]) =>{
nexts.push(obj.value);
if (obj.next){
flip(obj.next,nexts)
}
obj.value = nexts.shift()
return obj;
}
let result = flip(initObject)
console.log(result);
let original = flip(result)
console.log(original)

Convert string into nested objects and arrays

I am in a project where the previous guy did some strange routing and basically I get slugs from contentful in the form of strings which I put in an array ["/api/", "/docs/getting-started/", "/docs/, "/plugin/", "/plugin/user/", "/plugin/profile/"......]
Now I need to convert it in the form of an array
let cleanedSidebarContents = [{
title:null,
pages:[
{title:"api",
path:"/api/"
},
{title:"docs",
path:"/docs/"
},
{title:"plugin",
path:"/plugin/"
},
]
},
{
title:"plugin",
pages:[
{title:"user",
path:"/plugin/user/"
},
{title:"profile",
path:"/plugin/profile/"
},
]
},
{
title:"docs",
pages:[
{title:"getting-started",
path:"/plugin/getting-started/"
},
]
}
]
So currently what I am doing is this -
//-------Format sidebar data--------------->
let cleanedSidebarContents = [];
(function cleanSidebarContents() {
let sideBarContentsContentful = [];
cleanContentfulEdges.forEach(edge => {
let slug = edge.node.slug;
//split string into titles and remove empty spaces
let routeMapArray = slug.split("/").filter(x => x != "");
if (routeMapArray.length > 1) {
sideBarContentsContentful.push({
title: routeMapArray[0],
page: {
title: routeMapArray[routeMapArray.length - 1],
path: edge.node.slug
}
});
} else {
sideBarContentsContentful.push({
title: null,
page: {
title: routeMapArray[routeMapArray.length - 1],
path: edge.node.slug
}
});
}
});
let titles = [];
for (let i = 0; i < sideBarContentsContentful.length; i++) {
titles.push(sideBarContentsContentful[i].title);
}
//clean duplicate entries
titles = titles.filter(function (item, index, inputArray) {
return inputArray.indexOf(item) === index;
});
titles.sort();
titles.map(item => {
cleanedSidebarContents.push({
title: item,
pages: []
})
});
sideBarContentsContentful.forEach(item => {
for (let i = 0; i < cleanedSidebarContents.length; i++) {
if(cleanedSidebarContents[i].title === item.title){
cleanedSidebarContents[i].pages.push(item.page)
}
}
});
}());
//----------------------------------------->
I am first splitting all strings and putting the titles in a titles array then removing duplicates and mapping data accordingly.
I just feel like this is really bad code and there is a better way I just cannot figure out.
You could create a mapper object which maps the objects in the output with the title in the root of each object. split at the / to get all the chunks of path. .filter(Boolean) removes all the empty string created from beginning and end of the string. If there is only one match, then it belongs to the default null object. Else, use the first key in the matches as a key in the accumulator.
const input = ["/api/", "/docs/getting-started/", "/docs/", "/plugin/", "/plugin/user/", "/plugin/profile/"];
const mapper = input.reduce((acc, path) => {
let matches = path.split(/\//).filter(Boolean),
mapperKey = null;
if (matches.length > 1)
mapperKey = matches[0];
if (!acc[mapperKey])
acc[mapperKey] = { title: mapperKey, pages: [] };
const title = matches.pop();
acc[mapperKey].pages.push({ title, path });
return acc;
}, {})
const output = Object.values(mapper);
console.log(output)

Convert to Object and Adding property to object of array

I want to make filter by date with this object of array
const mapDateRange = () => {for (let elem in catchData) {
let x = {startDate:catchData[elem][0],finishDate:catchData[elem][1]};
return x;
}};
but its only catch one object of array
this is latest data has processing
const data = {
["01-08-2019", "08-08-2019"],
["08-08-2019", "15-08-2019"],
["15-08-2019", "22-08-2019"],
["22-08-2019", "29-08-2019"]
};
this is what i expected
const data = [
{
startDate:"01-08-2019", finisDate:"08-08-2019"
},
{
startDate:"08-08-2019", finisDate:"15-08-2019"
},
{
startDate:"15-08-2019", finisDate:"22-08-2019"
},
{
startDate:"22-08-2019", finisDate:"29-08-2019"
}
];
So there are a few problems in the code you wrote:
Your data started as an object ({}), but its built as an array, so I corrected that.
Your function mapDateRange uses catchData but it does not exist anywhere so I made the function except an argument, which will be the catchData.
Most important: You returned x which is only 1 item in the array of data. So I created an empty array and pushed x values to the array.
const data = [
["01-08-2019", "08-08-2019"],
["08-08-2019", "15-08-2019"],
["15-08-2019", "22-08-2019"],
["22-08-2019", "29-08-2019"]
];
const mapDateRange = (catchData) => {
let new_data = [];
for (let elem in catchData) {
let x = {
startDate: catchData[elem][0],
finishDate: catchData[elem][1]
};
new_data.push(x);
}
return new_data;
};
console.log(mapDateRange(data));
const data = [
["01-08-2019", "08-08-2019"],
["08-08-2019", "15-08-2019"],
["15-08-2019", "22-08-2019"],
["22-08-2019", "29-08-2019"]
];
const mapDataRange = (data) => {
const result = [];
data.forEach((item) => {
const x = { 'startDate': item[0], 'finishDate': item[1] };
result.push(x);
});
return result;
}
console.log(mapDatatRange(data));
In this way you will get your desire result by using map function
data = data.map((obj) => {
return {
startDate: obj[0],
finishDate: obj[1]
}
});
console.log(data)
try to do with .map and array destructuring with ES6 syntax
data.map(([ startDate, finishDate ]) => { startDate, finisDate })

Dynamically spread setState doesn't work on ternary operator spread

I have a nested state like this:
this.state = {
fields: {
subject: '',
from: {
name: '',
},
},
};
In an onChange function I'm handling updates to these nested values.
I'm trying to build a dynamically spread setState() for deep nests using dot notation.
For instance, with the array: const tree = ['fields','subject'] I'm able to update the subject state value with:
this.setState(prevState => ({
[tree[0]]: {
...prevState[tree[0]],
...(tree[2] ? {
...prevState[tree[1]],
[tree[2]]: value
}
: { [tree[1]]: value })
},
}));
Since the ternary operator is ending on { [tree[1]]: value }
But when my tree array is: const tree = ['fields','from','name'] the state value for fields.from.name is not changing, where it should be resolving to the first part of the ternary operator:
{
...prevState[tree[1]],
[tree[2]]: value
}
Am I missing something?
I've grown to prefer using libraries for these sorts of functions when it otherwise feels like I'm reinventing the wheel. lodash provides a set function (which also supports string paths):
_.set(object, path, value)
var object = { 'a': [{ 'b': { 'c': 3 } }] };
_.set(object, 'a[0].b.c', 4);
console.log(object.a[0].b.c);
// => 4
_.set(object, ['x', '0', 'y', 'z'], 5);
console.log(object.x[0].y.z);
// => 5
You'd also want to use _.cloneDeep(value) because _.set mutates the object.
this.state = {
fields: {
subject: '',
from: { name: '' },
},
};
const tree = ['fields', 'from', 'name']; // or 'fields.from.name'
this.setState(prevState => {
const prevState_ = _.cloneDeep(prevState);
return _.set(prevState_, tree, value);
});
You'll need a loop. For instance:
function update(prevState, tree, value) {
const newState = {};
let obj = newState;
for (let i = 0; i < tree.length; ++i) {
const name = tree[i];
if (i === tree.length - 1) {
obj[name] = value;
} else {
obj = obj[name] = {...prevState[name]};
}
}
return newState;
}
Live Example:
this.state = {
fields: {
subject: '',
from: {
name: '',
},
},
};
function update(prevState, tree, value) {
const newState = {};
let obj = newState;
let prev = prevState;
for (let i = 0; i < tree.length; ++i) {
const name = tree[i];
if (i === tree.length - 1) {
obj[name] = value;
} else {
const nextPrev = prev[name];
obj = obj[name] = {...nextPrev};
prev = nextPrev;
}
}
return newState;
}
const tree = ['fields','from','name']
const value = "updated";
console.log(update(this.state, tree, value));
I'm sure that can be shoehorned into a call to Array#reduce (because any array operation can be), but it wouldn't buy you anything.

How to get from an array of objects all unique values of a property that is an array itself

This answer is already close, and there are some answers how to get unique values in an array (remove duplicates,)though I can't make it work for the case where it is about an array of objects, and the property that should be filtered is an array. Sorry, I am a JS newbie. Thanks for the help.
I have an array of objects like this
const posts = [
post1: {
id: 1,
title: 'One',
tags: ['tagA', 'tagB']
},
post2: {
id: 2,
title: 'Two',
tags: ['tagB', 'tagC']
},
post3: {
id: 3,
title: 'Three',
tags: ['tagB', tagC, tagD]
]
What I would need is an array of all unique tags ... in the case above with an expected output like this:
// [tagA, tagB, tagC, tagD]
EDIT / UPDATE
The key in the array of objects is used to manage the state of the react component... e.g.
constructor(props) {
super(props);
this.state = {
posts: []
};
}
...
updatePost = (key, updatedPost) => {
//1. Take copy of the current this.state.
const posts = {...this.state.texts};
//2. Update that state
posts[key] = updatedPost;
//3. Set that to state
const options = { encrypt: false }
putFile(postsFileName, JSON.stringify(posts), options)
.then(() => {
this.setState({
posts: posts
})
})
};
Assuming that the input is on [ {} , {} ] format:
You can use concat and map to flatten your array. Use new Set to get the unique values.
const posts = [{"id":1,"title":"One","tags":["tagA","tagB"]},{"id":2,"title":"Two","tags":["tagB","tagC"]},{"id":3,"title":"Three","tags":["tagB","tagC","tagD"]}];
var result = [...new Set([].concat(...posts.map(o => o.tags)))];
console.log(result);
If the variable is an object ( {a:{} , b:{} } ) , you can use Object.values to convert the object into an array.
const posts = {"post1":{"id":1,"title":"One","tags":["tagA","tagB"]},"post2":{"id":2,"title":"Two","tags":["tagB","tagC"]},"post3":{"id":3,"title":"Three","tags":["tagB","tagC","tagD"]}}
var result = [...new Set([].concat(...Object.values(posts).map(o => o.tags)))];
console.log(result);
You can reduce your posts and iterate over the tags and push those to the result that you haven't encountered already:
const posts = [
{
id: 1,
title: "One",
tags: ["tagA", "tagB"]
},
{
id: 2,
title: "Two",
tags: ["tagB", "tagC"]
},
{
id: 3,
title: "Three",
tags: ["tagB", "tagC", "tagD"]
}
];
const uniqueTags = posts.reduce((result, post) => {
post.tags.forEach(tag => {
if (!result.includes(tag)) {
result.push(tag);
}
});
return result;
}, []);
console.log(uniqueTags);
This is assuming you know that the array key is always 'tags'.
let filter = {};
let result = [];
posts.forEach(post => {
const tags = post['tags'];
tags.forEach(tag => {
if (!filter.hasOwnProperty(tag)) {
result.push(tag);
filter[tag] = true;
}
});
});
with jquery you can do something similar to this (not Tested):
var results = [];
$.each(myObject, function(key,valueObj){
var check.isArray(obj);
if(check){
alert(key + "/" + valueObj );
/*replace repeat*/
var sorted_check = check.slice().sort(); // You can define the comparing function here.
// JS by default uses a crappy string compare.
// (we use slice to clone the array so the
// original array won't be modified)
for (var i = 0; i < sorted_check.length - 1; i++) {
if (sorted_check[i + 1] == sorted_check[i]) {
results.push(sorted_check[i]);
}
}
}
});
and a good way with indexof:
Array.prototype.unique = function() {
var a = [];
for ( i = 0; i < this.length; i++ ) {
var current = this[i];
if (a.indexOf(current) < 0) a.push(current);
}
this.length = 0;
for ( i = 0; i < a.length; i++ ) {
this.push( a[i] );
}
return this;
}
Array.prototype.unique = function() {
var a = [];
for ( i = 0; i < this.length; i++ ) {
var current = this[i];
if (a.indexOf(current) < 0) a.push(current);
}
return a;
}
And Follow UP:
Array.prototype.unique = function(mutate) {
var unique = this.reduce(function(accum, current) {
if (accum.indexOf(current) < 0) {
accum.push(current);
}
return accum;
}, []);
if (mutate) {
this.length = 0;
for (let i = 0; i < unique.length; ++i) {
this.push(unique[i]);
}
return this;
}
return unique;
}
If you want to use a functional library like Ramda.js you can do this:
const posts = [
{
id: 1,
title: 'One',
tags: ['tagA', 'tagB'],
},
{
id: 2,
title: 'Two',
tags: ['tagB', 'tagC'],
},
{
id: 3,
title: 'Three',
tags: ['tagB', 'tagC', 'tagD'],
},
];
var unique = R.uniq(R.flatten(R.map(R.prop('tags'), posts)))
console.log(unique)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>

Categories