Get all the objects from state which is having multiple arrays - javascript

Actually I have a state data which is an object , It has following structutre,
{ one : [ { abc:1 }, { abc: 2 }], two : [ { abc:3 }, { abc: 4 }, three : [ { abc:5 }, { abc: 6 }]] }
So its like an arry of objects in an state object .
Now, I want to create an array of objects which will have all these objects .
So I want to have it like,
[{ abc:1 }, { abc: 2 },{ abc:3 }, { abc: 4 },{ abc:5 }, { abc: 6 }]
The way I tried is using for loop.
let quizCriteriaObj = [];
let low = this.props.lowQuizData["Low"];
let High = this.props.lowQuizData["High"];
let Medium = this.props.lowQuizData["Medium"];
console.log("data is ", low);
for (let i = 0; i <= low.length - 1; i++) {
quizCriteriaObj.push(low[i]);
}
for (let i = 0; i <= High.length - 1; i++) {
quizCriteriaObj.push(High[i]);
}
for (let i = 0; i <= Medium.length - 1; i++) {
quizCriteriaObj.push(Medium[i]);
}
console.log(quizCriteriaObj);
I have taken each field aside from that object and using a for loop on every field. SO, It is working for me. But , I think this is not a proper solution for me .Is there any thing That I am doing wrong ?

Modern javascript makes this trivial
Array.prototype.flat
Please note: Array.prototype.flat is a stage 3 TC39 proposal, so is not part of the ECMAScript specification (yet)
It is supported in all modern browsers (and can be polyfilled for Microsofts attempts at browsers, both Internet Explorer and Edgey)
Note: I assume you mistyped the "source" object, because as it was, it was invalid
let obj = {
one : [
{ abc:1 },
{ abc: 2 }
],
two : [
{ abc:3 },
{ abc: 4 }
],
three : [
{ abc:5 },
{ abc: 6 }
]
}
let ftw = Object.values(obj).flat(); //<== single line of code is all you need
console.log(JSON.stringify(ftw))

You can use Object.values to convert the object into an array. Use spread syntax and concat() to flatten the array
var obj ={"one":[{"abc":1},{"abc":2}],"two":[{"abc":3},{"abc":4}],"three":[{"abc":5},{"abc":6}]}
var result = [].concat(...Object.values(obj));
console.log(result);

You can use a for...in loop
let obj = { one : [ { abc:1 }, { abc: 2 }], two : [ { abc:3 }, { abc: 4 }], three : [ { abc:5 }, { abc: 6 }] };
let result = [];
for (let key in obj) result = [...result, ...obj[key]];
console.log(result);

Using lodash is simple:
const obj = { one : [ { abc:1 }, { abc: 2 }], two : [ { abc:3 }, { abc: 4 }, three : [ { abc:5 }, { abc: 6 }]] }
_.flatten(obj)
// return => [{ abc:1 }, { abc: 2 },{ abc:3 }, { abc: 4 },{ abc:5 }, { abc: 6 }]

Good question you can't simply spread the arrays so a good one liner
for this would be.
state = {
one : [ { abc:1 }, { abc: 2 }],
two : [ { abc:3 }, { abc: 4 }],
three : [ { abc:5 }, { abc: 6 }]
}
var result = Object.values(state).flat() // solution
console.log(result)

Related

Group the parent object array based on the filter of the child object array

I've coded and I've got the results I want, but I think this code looks unclean.
const verses = [{
index: 1,
verses: [{
level: 1
},
{
level: 1
}
]
},
{
index: 2,
verses: [{
level: 1
},
{
level: 1
}
]
},
{
index: 3,
verses: [{
level: 2
}]
},
{
index: 4,
verses: [{
level: 2
}]
},
{
index: 5,
verses: [{
level: 2
},
{
level: 2
}
]
},
{
index: 6,
verses: [{
level: 3
},
{
level: 3
}
]
},
{
index: 7,
verses: [{
level: 3
},
{
level: 3
},
{
level: 4
},
{
level: 4
}
]
}
]
function getVerseIndexByLevel(level) {
const result = verses.map(v => {
const mappingByLevel = Math.max.apply(Math, [...new Set(v.verses.map(w => w.level))])
const mappingByVerse = v.index
return {
level: mappingByLevel,
verse: mappingByVerse
}
}).filter(v => v.level === level).map(v => v.verse)
return result
}
for (let i = 1; i <= 4; i++) {
console.log({
level: i,
verse: getVerseIndexByLevel(i)
})
}
Is my code above consuming too much performance?
Could you make it cleaner and simpler without changing the result at all?
-I don't know what details I have to add more, StackOverflow forced me to write this because the post is mostly code. but I hope you understand just by looking at the results of the code.-
On the performance end, the one thing I can see that could improve it would be to call getVerseIndexByLevel only once, and use the calculated result mapping, instead of calling it multiple times (requiring re-parsing the input every time). Something like
const verseIndicies = getVerseIndicies();
for (let i = 1; i <= 4; i++) {
console.log({
level: i,
verse: verseIndicies[i]
})
}
Another slight improvement - since you're using ES6 syntax, you don't need to .apply to Math.max - just spread the set into it.
You also don't need to turn the level numbers into a Set first - since it's being passed into Math.max anyway, repetitions aren't an issue.
const verses=[{index:1,verses:[{level:1},{level:1}]},{index:2,verses:[{level:1},{level:1}]},{index:3,verses:[{level:2}]},{index:4,verses:[{level:2}]},{index:5,verses:[{level:2},{level:2}]},{index:6,verses:[{level:3},{level:3}]},{index:7,verses:[{level:3},{level:3},{level:4},{level:4}]}];
function getVerseIndicies() {
const verseIndicies = {};
for (const v of verses) {
const level = Math.max(...v.verses.map(w => w.level));
verseIndicies[level] ??= [];
verseIndicies[level].push(v.index);
}
return verseIndicies;
}
const verseIndicies = getVerseIndicies();
for (let i = 1; i <= 4; i++) {
console.log({
level: i,
verse: verseIndicies[i]
})
}

Javascript - How to make all object items set with different values?

NOTE: This is the first question that I ever created in StackOverflow.
I was trying to implement layer manager in javascript with this value:
var layers = [
{
id: "layer_1",
name: "Layer 1",
child: [
{
id: "layer_1A",
name: "Layer 1A",
child: [
{
id: "layer_1A1",
name: "Layer 1A1"
},
{
id: "layer_1A2",
name: "Layer 1A2"
}
]
},
{
id: "layer_1B",
name: "Layer 1B"
}
]
},
{
id: "layer_2",
name: "Layer 2"
},
{
id: "layer_3",
name: "Layer 3"
}
]
and save the result into this:
var indexSelector = {};
here is the code:
var value = [-1];
function read_layers(obj) {
// Checks the array (layer/child)
if (Array.isArray(obj)) {
for (let layer of obj) {
read_layers(layer);
}
}
// Checks the object
else if (typeof obj == "object") {
for (let layer of Object.entries(obj)) {
// Checks the childs
if (layer[0] == "layers" || layer[0] == "child") {
value.push(-1);
read_layers(obj[layer[0]]);
value.pop()
}
// Checks the object keys
else if (layer[0] == "id") {
if (!indexSelector.hasOwnProperty(layer[1])) {
++value[value.length-1];
indexSelector[layer[1]] = value; // Result
}
}
}
}
}
read_layers(layers);
I want the expected result to looks like this:
{
layer_1: [ 0 ]
layer_1A: [ 0, 0 ]
layer_1A1: [ 0, 0, 0 ]
layer_1A2: [ 0, 0, 1 ]
layer_1B: [ 0, 1 ]
layer_2: [ 1 ]
layer_3: [ 2 ]
}
But here is the problem result:
{
layer_1: [ 2 ]
layer_1A: [ 2 ]
layer_1A1: [ 2 ]
layer_1A2: [ 2 ]
layer_1B: [ 2 ]
layer_2: [ 2 ]
layer_3: [ 2 ]
}
How to fix this problem with different object values? Thanks.
NOTE: All assignment (with operator = ) of objects is applied with theirs reference, in javascript, like in as java.
So, at line indexSelector[layer[1]] = value; // Result , the value is assigned with its reference, not the whole value.
That's why the values of the result are all same.
Solution: Use indexSelector[layer[1]] = Object.assign({}, value);.
It'll solve your problem.

Generating a Nested Set Model from a POJO

I have been playing around with some Nested Set Models (NSM). One thing I wanted to do is to be able to generate a NSM from a given JavaScript object.
For example, given the following object:
var data = {
Clothes: {
Jackets: {
Waterproof: true,
Insulated: true
},
Hats: true,
Socks: true
},
}
I'd like to generate an array of objects like so.
[
{
"name": "Clothes",
"lft": 1,
"rgt": 12
},
{
"name": "Jackets",
"lft": 2,
"rgt": 7
},
{
"name": "Waterproof",
"lft": 3,
"rgt": 4
},
{
"name": "Insulated",
"lft": 5,
"rgt": 6
},
{
"name": "Hats",
"lft": 8,
"rgt": 9
},
{
"name": "Socks",
"lft": 10,
"rgt": 11
}
]
That is - a depth first walk through the object, assigning an ID and counting the left and right edge for each object in the hierarchy. So that each node has a unique ID and the correct lft and rgt values for a NSM.
I've tried various approaches but just can't seem to get the result I am after...I had some success by altering the model to use properties for the node name and child nodes - i.e.
var data2 = {
name: "Clothes",
children: [{
name: "Jackets",
children: [{
name: "Waterproof",
}, {
name: "Insulated"
}]
}, {
name: "Hats"
},
{
name: "Socks"
}
]
};
function nestedSet(o, c, l = 0) {
let n = {
name: o.name,
lft: l + 1
};
c.push(n);
let r = n.lft;
for (var x in o.children) {
r = nestedSet(o.children[x], c, r);
}
n.rgt = r + 1;
return n.rgt;
}
let out = [];
nestedSet(data2, out);
console.log(out)
This gives the correct result but requires altering the input data...is there a way to generate the same Nested Set Model using the original data object?
I actually managed to solve this in the end...I just forgot about it for a long while! Basically all that is required is to reclusively pass the Object.entries as kindly suggested in #CherryDT's comment. This way one can resolve the name/children to build the nested set model as required.
var data = {
Clothes: {
Jackets: {
Waterproof: {},
Insulated: {},
},
Hats: {},
Socks: {},
},
};
function ns(node, stack = [], lft = 0) {
var rgt = ++lft;
var item = {
name: node[0],
lft: lft,
};
stack.push(item);
Object.entries(node[1]).forEach(function (c) {
rgt = ns(c, stack, rgt);
});
item.rgt = ++rgt;
return rgt;
}
var result = [];
ns(Object.entries(data)[0], result);
console.log(result);

Lodash - DifferenceBy with different identity

I have the following array:
[
{
id: 1
},
{
id: 2
},
{
id: 3
},
{
id: 4
}
]
Every 5 seconds my application receives a new array and I need to compare the difference between the next one...
So the next array is:
[
{
conversation_id: 1
},
{
conversation_id: 2
},
{
conversation_id: 4
}
]
Considering that identity is different. How can I compare with the previous and get an array with the excluded item?
[
{
id: 3
}
]
Use _.differenceWith():
const prev = [{"id":1},{"id":2},{"id":3},{"id":4}]
const next = [{"conversation_id":1},{"conversation_id":2},{"conversation_id":4}]
const diff = _.differenceWith(prev, next, ({ id }, { conversation_id }) => _.eq(id, conversation_id))
console.log(diff)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.js"></script>
I think you can use mix of javascript and lodash to solve this problem.
var arrayList = [
{
id: 1
},
{
id: 2
},
{
id: 3
},
{
id: 4
}
];
var conv_array = [
{
conversation_id: 1
},
{
conversation_id: 2
},
{
conversation_id: 4
}
];
var itemsNotInArray = [];
arrayList.filter(function (item) {
if (!_.find(conv_array, {conversation_id: item.id })) {
console.log("not in array", item);
itemsNotInArray.push(item);
}
});
console.log("result you expected", itemsNotInArray);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
Filter the first array and compare each value till you find a missing id :
var array1 = [{
id: 1
},
{
id: 2
},
{
id: 3
},
{
id: 4
}
];
var array2 = [{
conversation_id: 1
},
{
conversation_id: 2
},
{
conversation_id: 4
}
];
var test = array1.filter(
conv => !array2.find(
id => id.conversation_id === conv.id
)
);
console.log(test)
From lodash documentation, the third argument to differenceBy is
[iteratee=_.identity] (Function): The iteratee invoked per element.
Based on this, you can use
var current = [
{
id: 1
},
{
id: 2
},
{
id: 3
},
{
id: 4
}
];
and
var next = [
{
conversation_id: 1
},
{
conversation_id: 2
},
{
conversation_id: 4
}
];
then
var difference = _.differenceBy(current, next, function(obj) {
return obj.id || obj.conversation_id;
});
Or shortened with an arrow function:
var difference = _.differenceBy(current, next, (x) => x.id || x.conversation_id)

Javascript/Lodash - Getting unique values in an Array of arrays

I am having a complicated case where I need to have unique values in an array of arrays.
here is what I have right now :
var ar = [{
"theFirstArray": [
[{
"no": 1
}, {
"no": 2
}, {
"no": 3
}, {
"no": 4
}, {
"no": 11
}]
],
"theSecondArray": [{
"no": 3
},
{
"no": 1
},
{
"no": 4
},
{
"no": 9
},
{
"no": 39
},
{
"no": 18
},
{
"no": 19
}
],
"theThirdArray": [{
"no": 20
}, {
"no": 12
}, {
"no": 10
}, {
"no": 9
}, {
"no": 16
}]
}]
What I need to achieve :
[{
"theFirstArray":[{"no":1},{"no":2},{"no":3},{"no":4}]
},{
"theSecondArray":[{"no":9}, {"no":39}, {"no":18}, {"no":19}]
},{
"theThirdArray":[{"no":20},{"no":12},{"no":10},{"no":16}]
}]
PS. note that I need only to output 4 values in each subarray, and also the value is not repeated in all the array.
I've already tried the following:
_.uniqBy(ar, 'no');
It's true that I got only the unique values in the result; however, I was not able to get the same wanted structure as I got the following:
[{
theFirstArray:[{"no":1,"no":2,"no":3,"no":4, "no":9, "no":39, "no":18, "no":19, "no":20,"no":12,"no":10,"no":16}]
}]
I've also tried to work with _.map but that didn't work to!
You can use something like this:
function getUniqueArrays(arr) {
var uniqAr = [];
var allArr = [{
no: null
}];
arr.forEach(function(item) {
var itemKey = Object.keys(item)[0];
var itemArr = _.uniqBy(item[itemKey], 'no');
var itemArr = _.differenceBy(itemArr, allArr, 'no').slice(0, 4);
allArr = itemArr.concat(allArr);
var newItem = {};
newItem[itemKey] = itemArr;
uniqAr.push(newItem);
})
return uniqAr;
}
From your example I presume, you always has only one key in object.
If you always have unique values in every array (that one that contain objects like {no: 1}), than you can get rid off line:
var itemArr = _.uniqBy(item[itemKey], 'no');
Lodash all the things! You can use reduce to store already seen values and cleanup them with uniqBy, and get only needed ones with differenceBy for each loop cycle.
Here is how it could look:
_.map(items, item => _.reduce(
_.toPairs(item),
({ seenValues, result }, [key, values]) => ({
seenValues: _.uniqBy([...seenValues, ...values], 'no'),
result: {
...result,
[key]: _.take(_.differenceBy(values, seenValues, 'no'), 4)
}
}),
{ seenValues: [], result: {} }
));
We use toPairs to have that unpacked key and value in reduce function, so we can costruct needed structure back.
At the end you can just get the result property. Check this jsbin: http://jsbin.com/tikoyehapi/1/edit?js,console for more.

Categories

Resources