Create new object out of huge array of objects - javascript

I retrieve a huge array from the server (contains 2000+ objects). the structure of this array is like this:
const arr = [
{code: 'A', text:'blabla', codeParent:'-'},
{code: 'B', text:'blabla', codeParent:'-'},
{code: 'C', text:'blabla', codeParent:'-'},
{code: 'D', text:'blabla', codeParent:'-'},
{code: '01', text:'blabla', codeParent:'B'},
{code: '02', text:'blabla', codeParent:'C'},
{code: '03', text:'blabla', codeParent:'D'},
{code: '04', text:'blabla', codeParent:'A'},
{code: '05', text:'blabla', codeParent:'A'},
{code: '06', text:'blabla', codeParent:'B'},
...
]
The object that I want to make looks like this:
const obj = {
A: [array of all object with codeParent 'A'],
B: [array of all object with codeParent 'B'],
C: [array of all object with codeParent 'C'],
D: [array of all object with codeParent 'D']
}
Does anyone know the best way to get this result?

Assuming a couple things about your output that weren't entirely clear from your explanation, but appear to be the case from your sample output:
The output is collated by the codeParent property
You are skipping items that have codeParent: '-'
Then, you can just iterate through the big array and populate the output object as you go:
let result = {};
for (item of arr) {
let parent = item.codeParent;
if (parent !== '-') {
let destArray = obj[parent];
// if no array yet for this code, create one
if (!destArray) {
destArray = [];
obj[parent] = destArray;
}
// add the new item to the destination array
destArray.push(item);
}
}
console.log(result);
Some people will prefer to use .reduce() for this:
let result = arr.reduce((obj, item) => {
let parent = item.codeParent;
if (parent !== '-') {
let destArray = obj[parent];
// if no array yet for this code, create one
if (!destArray) {
destArray = [];
obj[parent] = destArray;
}
// add the new item to the destination array
destArray.push(item);
}
return obj;
}, {});
Note, each loop has slightly more code because I'm trying to only have to look up obj[item.codeParent] once for each iteration rather than multiple times and also trying to only look up the codeParent propery in item.codeParent once too. You could do it with less code like this and slightly less efficient:
let result = arr.reduce((obj, item) => {
if (item.codeParent !== '-') {
if (!obj[item.codeParent]) {
obj[item.codeParent] = [];
}
obj[item.codeParent].push(item);
}
return obj;
}, {});
Note that this looks up obj[item.codeParent] 2 or 3 times in every iteration whereas the previous two versions look it up 1 or 2 times.

Related

How to remove empty elements in a multidimensional array using Javascript

Beginner programmer here that is trying to build a tool to make my life easier. I am able to pull data from a Google sheet, but it comes out looking like the bellow array with a lot of empty elements both in the elements I want to capture (Person, Person2, etc.) and around them. This is due to how the sheet is formatted and I won't be able to remove the empty cells around it.
var array = [[Person1,, Age1,, Address1], [,,,,], [Person2,, Age2,, Address2], [,,,,] ...]
I assume there is an easy way to filter through the array and remove the empty/null items. But my attempts to use .filter() and nested for loops have not worked. Can anyone help on the best way to get a multidimensional array without the empty items?
you can use reduce function and remove items which are either null or array with zero length
var arr = [["Person1", null, "Age1", null, "Address1"]
, [null, null, null, null, null]
, ["Person2", null, "Age2", null, "Address2"],
[null, null, null, null, ["t"]]]
function reducer(res, item) {
if (!item) return res;
if (Array.isArray(item)) {
var obj = item.reduce(reducer, [])
if (obj.length > 0) {
res.push(obj)
}
return res;
}
res.push(item);
return res;
}
var res = arr.reduce(reducer , [])
console.log(res)
Fortunately you have just a 2D array, which is a list of 1D arrays.
Let's start with an 1D array:
var row = ['a','b',,'c',,];
// via a loop:
var new_row = [];
for (cel in row) if (row[cel]) new_row.push(row[cel]);
console.log(new_row); // ['а', 'b', 'c']
// via a filter() function:
var new_row_f = row.filter((cel) => cel);
console.log(new_row_f); // ['a', 'b', 'c']
Here is a 2D array:
var table = [['a1','b1',,'c1',,],[,,,,,],['a2','b2',,'c2',,]]
// via nested loops:
var new_table = []
for (var row=0; row<table.length; row++) {
var new_row = [];
for (var cel=0; cel<table[row].length; cel++) {
var new_cel = table[row][cel];
if (new_cel) new_row.push(new_cel);
}
if (new_row.join("")!="") new_table.push(new_row);
}
console.log(new_table); // [ [ 'a1', 'b1', 'c1' ], [ 'a2', 'b2', 'c2' ] ]
// via a chain of filter() & map(filter()) functions:
var new_table_f = table.filter(row => row.join("") != "")
.map(row => row.filter((cel) => cel));
console.log(new_table_f); // [ [ 'a1', 'b1', 'c1' ], [ 'a2', 'b2', 'c2' ] ]

Create multidimensional arry without identifier

I'd like to use a multidimensional arry in JS. The array looks like this:
var name = {
abc: {
0: 'a',
1: 'b',
2: 'c'
}
};
This works like a charm. But is it possible to create the same array without the numbers? Like this:
var name = {
abc: {
'a',
'b',
'c'
}
};
I still need the same access to it (e.q. name['abc'][1] = b)
Use an actual array instead of an object for abc?
var name = {
abc: [
'a',
'b',
'c'
]
};

delete property with sequential index in object

i have an object like below:
item = {
0: 'A',
1: 'B',
2: 'C',
3: 'D'
}
For e.g., if i would like to delete '1' from the item object and after it was deleted, '2' and '3' should auto minus 1 and become object below:
item = {
0: 'A',
1: 'C',
2: 'D'
}
Is it possible to achieve this with object beside using array?
Omit the key you don't want, convert to array using Object.values(), and then convert back to object by spreading to an empty object:
const item = {
0: 'A',
1: 'B',
2: 'C',
3: 'D'
}
const result = { ...Object.values(_.omit(item, 1)) }
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
Or you can create a function that uses rest params:
const fn = (key, { [key]: _, ...rest }) => ({ ...Object.values(rest) })
const item = {
0: 'A',
1: 'B',
2: 'C',
3: 'D'
}
const result = fn(1, item)
console.log(result)
Use a delete statement
delete item["1"];
You can use an array for removing and for re-indexing purpose.
let item = {
0: 'A',
1: 'B',
2: 'C',
3: 'D'
};
// get values as an array
let arr = Object.values(item);
// remove element at index 1
arr.splice(1, 1);
// extract array to an object
item = {
...arr
};
console.log(item);
Here another solution in case you don't want to rely in third party plugins. Lodash is quite a heavy library as well.
var deleteObj = ({data, index}) => {
var objSize = Object.keys(data).length
if (index > objSize -1) return data
var count = index;
delete data[count];
count = count+1;
while (count < objSize){
data[count-1] = data[count];
count = count+1;
}
delete data[count-1];
return data;
}
deleteObj({data: {0: "A", 1: "B", 2: "C", 3: "D", 4: "E", 5: "F"}, index: 0});
Or else you can use lodash's filter directly on object (actually you can) to remove unnecessery items and then produce an array in one step, and then just use object spread operator to build back the resultant object.
Example:
let o = {"0":"A","1":"B","2":"C","3":"D"},
res = {..._.filter(o, (v, k) => k!=1)};
console.log(res);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>

How to check if array contains at least one object ?

I want to check if array contains object or not. I am not trying to compare values just want to check in my array if object is present or not?
Ex.
$arr = ['a','b','c'] // normal
$arr = [{ id: 1}, {id: 2}] // array of objects
$arr = [{id: 1}, {id:2}, 'a', 'b'] // mix values
So how can i check if array contains object
You can use some method which tests whether at least one element in the array passes the test implemented by the provided function.
let arr = [{id: 1}, {id:2}, 'a', 'b'];
let exists = arr.some(a => typeof a == 'object');
console.log(exists);
I want to check if array contains object or not
Use some to simply check if any item of the array has value of type "object"
var hasObject = $arr.some( function(val){
return typeof val == "object";
});
var hasObject = function(arr) {
for (var i=0; i<arr.length; i++) {
if (typeof arr[i] == 'object') {
return true;
}
}
return false;
};
console.log(hasObject(['a','b','c']));
console.log(hasObject([{ id: 1}, {id: 2}]));
console.log(hasObject([{id: 1}, {id:2}, 'a', 'b']));
You could count the objects and use it for one of the three types to return.
function getType(array) {
var count = array.reduce(function (r, a) {
return r + (typeof a === 'object');
}, 0);
return count === array.length
? 'array of objects'
: count
? 'mix values'
: 'normal';
}
console.log([
['a', 'b', 'c'],
[{ id: 1 }, { id: 2 }],
[{ id: 1 }, { id: 2 }, 'a', 'b']
].map(getType));
With a type check on array
const hasObject = a => Array.isArray(a) && a.some(val => typeof val === 'object')

How do I get a specific object from an immutable js map by value?

I created an immutable map (with Immutable-JS) from a list of objects:
var result = [{'id': 2}, {'id': 4}];
var map = Immutable.fromJS(result);
Now i want to get the object with id = 4.
Is there an easier way than this:
var object = map.filter(function(obj){
return obj.get('id') === 4
}).first();
Essentially, no: you're performing a list lookup by value, not by index, so it will always be a linear traversal.
An improvement would be to use find instead of filter:
var result = map.find(function(obj){return obj.get('id') === 4;});
The first thing to note is that you're not actually creating a map, you're creating a list:
var result = [{'id': 2}, {'id': 4}];
var map = Immutable.fromJS(result);
Immutable.Map.isMap(map); // false
Immutable.List.isList(map); // true
In order to create a map you can use a reviver argument in your toJS call (docs), but it's certainly not the most intuitive api, alternatively you can do something like:
// lets use letters rather than numbers as numbers get coerced to strings anyway
var result = [{'id': 'a'}, {'id': 'b'}];
var map = Immutable.Map(result.reduce(function(previous, current) {
previous[ current.id ] = current;
return previous;
}, {}));
Immutable.Map.isMap(map); // true
Now we have a proper Immutable.js map which has a get method
var item = Map.get('a'); // {id: 'a'}
It may be important to guarantee the order of the array. If that's the case:
Use an OrderedMap
Do a set method on the OrderedMap at each iteration of your source array
The example below uses "withMutations" for better performance.
var OrderedMap = Immutable.OrderedMap
// Get new OrderedMap
function getOm(arr) {
return OrderedMap().withMutations(map => {
arr.forEach(item => map.set(item.id, item))
})
}
// Source collection
var srcArray = [
{
id: 123,
value: 'foo'
},
{
id: 456,
value: 'bar'
}
]
var myOrderedMap = getOm(srcArray)
myOrderedMap.get(123)
// --> { id: 123, value: 'foo' }
myOrderedMap.toObject()
// --> { 123: {id: 123, value: 'foo'}, 456: {id: 456, value: 'bar'} }
myOrderedMap.toArray()
// --> [ {id: 123, value: 'foo'}, { id: 456, value: 'bar' } ]
When using fromJS for array, you'll get List not map. It will be better and easier if you create a map. The following code will convert the result into Immutable map.
const map = result.reduce((map, json) =>
map.set(json.id, Immutable.fromJS(json))
, Map());
Now, you can
map.get('2'); //{'id': 2}
Note, if the result has nested structure and if that has array, it will be a List with the above code.
With ES2015 syntax (and constants):
const result = map.find(o => o.get('id') === 4);
Is there already a way thats easier? I don't know. but you can write your own function. Something like this should work:
var myFunc = function(id){
var object = map.filter(function(obj){return obj.get('id') === id}).first();
return object;
}
Then you would just do:
var myObj = myFunc(4);

Categories

Resources