get the array objects in the exact index - javascript

I have an array
let items=[5,1,3,4,2]
and an object array
let objectItem=[
{id:"item-1", name: "Total revenue",selected:true, order: 1},
{id:"item-2", name: "Total tax",selected:true, order: 2},
{id:"item-3", name: "Sales Report",selected:false, order: 3},
{id:"item-4", name: "Total parking",selected:false, order: 4},
{id:"item-5", name: "Sales maximum",selected:false, order: 5},
{id:"item-6", name: "Amount Collected",selected:true, order: 6},
{id:"item-7", name: "Total fall",selected:false, order: 7}
]
i need to get the array objectItem in the order in which it matches the order key with the item array element in the exact order.
my resultant array should be like this,
objectItem=[
{id:"item-5", name: "Sales maximum",selected:false, order: 5},
{id:"item-1", name: "Total revenue",selected:true, order: 1},
{id:"item-3", name: "Sales Report",selected:false, order: 3},
{id:"item-4", name: "Total parking",selected:false, order: 4},
{id:"item-2", name: "Total tax",selected:true, order: 2},
]
so far i was able to acheieve the object item array in the ascending order of order key after applying
const itemArr=objectItem.filter((itm)=>{
return items.indexOf(itm.order) > -1;
});
is there any work around to obtain the resulting array with the objects with order value as in the exact index of the items array

let items=[5,1,3,4,2]
let objectItem=[
{id:"item-1", name: "Total revenue",selected:true, order: 1},
{id:"item-2", name: "Total tax",selected:true, order: 2},
{id:"item-3", name: "Sales Report",selected:false, order: 3},
{id:"item-4", name: "Total parking",selected:false, order: 4},
{id:"item-5", name: "Sales maximum",selected:false, order: 5},
{id:"item-6", name: "Amount Collected",selected:true, order: 6},
{id:"item-7", name: "Total fall",selected:false, order: 7}
]
console.log(items.map(i=>objectItem.find(({order:o})=>o===i)))

You could take an object and get the items in wanted order.
const
items = [5, 1, 3, 4, 2],
objectItem = [{ id: "item-1", name: "Total revenue", selected: true, order: 1 }, { id: "item-2", name: "Total tax", selected: true, order: 2 }, { id: "item-3", name: "Sales Report", selected: false, order: 3 }, { id: "item-4", name: "Total parking", selected: false, order: 4 }, { id: "item-5", name: "Sales maximum", selected: false, order: 5 }, { id: "item-6", name: "Amount Collected", selected: true, order: 6 }, { id: "item-7", name: "Total fall", selected: false, order: 7 }],
hash = Object.fromEntries(objectItem.map(o => [o.id, o])),
result = items.map(i => hash[`item-${i}`]);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

An approach which avoids the almost quadratic effort of a mapping task that for each iteration step depends on a nested also iterating find task can be based on two separate (not nested) iteration cycles ...
a reduce task which creates a Map and order-key based item-lookup.
the final map task which creates a correctly ordered item-list by iterating the item-precedence array/list and obtaining each item by the currently processed order-value where the latter is the lookup's key.
const listOfItems = [
{ id: "item-1", name: "Total revenue", selected: true, order: 1 },
{ id: "item-2", name: "Total tax", selected: true, order: 2 },
{ id: "item-3", name: "Sales Report", selected: false, order: 3 },
{ id: "item-4", name: "Total parking", selected: false, order: 4 },
{ id: "item-5", name: "Sales maximum", selected: false, order: 5 },
{ id: "item-6", name: "Amount Collected", selected: true, order: 6 },
{ id: "item-7", name: "Total fall", selected: false, order: 7 },
];
const itemPrecedenceList = [5, 1, 3, 4, 2];
// - one-time iteration in order to create a
// `Map` and `order`-key based `item`-lookup.
const itemLookup = listOfItems
.reduce((lookup, item) => {
lookup.set(item.order, item);
return lookup;
}, new Map);
// - one-time iteration in order to
// create a correctly ordered item-list.
const reorderedItems = itemPrecedenceList
.map(order => itemLookup.get(order));
console.log({
reorderedItems,
itemPrecedenceList,
});
.as-console-wrapper { min-height: 100%!important; top: 0; }
And in case the OP wants to mutate the original items array by sorting it in place, the above implementation changes to ...
a single reduce task again, where this time a Map and order-key based new precedence-lookup is going to be created.
the final sort task which is less efficient than the one-time iteration before, but does accomplish the array's in-place sort followed by the immediate truncation of all not needed items via the arrays length property.
const listOfItems = [
{ id: "item-6", name: "Amount Collected", selected: true, order: 6 },
{ id: "item-7", name: "Total fall", selected: false, order: 7 },
{ id: "item-1", name: "Total revenue", selected: true, order: 1 },
{ id: "item-2", name: "Total tax", selected: true, order: 2 },
{ id: "item-3", name: "Sales Report", selected: false, order: 3 },
{ id: "item-4", name: "Total parking", selected: false, order: 4 },
{ id: "item-5", name: "Sales maximum", selected: false, order: 5 },
];
const itemPrecedenceList = [5, 1, 3, 4, 2];
// - one-time iteration in order to create a `Map`
// and `order`-key based new precedence lookup.
const precedenceLookup = itemPrecedenceList
.reduce((lookup, order, idx) => {
lookup.set(order, idx);
return lookup;
}, new Map);
// - `sort` task which is less efficient than the one-time
// iteration but does accomplish the array's in-place sort ...
listOfItems
.sort((a, b) =>
(precedenceLookup.get(a.order) ?? Number.MAX_SAFE_INTEGER) -
(precedenceLookup.get(b.order) ?? Number.MAX_SAFE_INTEGER)
)
// ... with the immediate truncation of all not needed items.
.length = itemPrecedenceList.length;
console.log({
listOfItems,
itemPrecedenceList,
});
.as-console-wrapper { min-height: 100%!important; top: 0; }

Related

How to build a tree of objects out of a flat array of records in JavaScript?

How do I implement this properly?
const tree = buildTree(1, shuffleArray([
{ type: 'string', source_id: 1, name: 'foo', value: 'asdf' },
{ type: 'integer', source_id: 1, name: 'bar', value: 123 },
{ type: 'object', source_id: 1, name: 'nested', value: 2 },
{ type: 'object', source_id: 2, name: 'nested', value: 3, array: true },
{ type: 'boolean', source_id: 3, name: 'random', value: true },
{ type: 'string', source_id: 3, name: 'another', value: 'hello' },
{ type: 'object', source_id: 2, name: 'nested', value: 4, array: true },
{ type: 'boolean', source_id: 4, name: 'random', value: false },
{ type: 'string', source_id: 4, name: 'another', value: 'world' },
{ type: 'object', source_id: 2, name: 'nested', value: 5, array: true },
{ type: 'boolean', source_id: 5, name: 'random', value: true },
{ type: 'string', source_id: 5, name: 'another', value: 'awesome' },
]))
function buildTree(startId, array) {
const map = array.reduce((m, x) => {
m[x.source_id] = m[x.source_id] ?? {}
if (x.array) {
m[x.source_id][x.name] = m[x.source_id][x.name] ?? []
m[x.source_id][x.name].push({ id: x.value })
} else {
m[x.source_id][x.name] = x.value
}
return m
}, {})
// ??? getting lost...
}
function shuffleArray(array) {
for (var i = array.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array
}
where the "expected tree" would be something like this:
const expectedTree = {
id: 1,
foo: 'asdf',
bar: 123,
nested: {
id: 2,
nested: [
{
id: 3,
random: true,
another: 'hello'
},
{
id: 4,
random: false,
another: 'world'
},
{
id: 5,
random: true,
another: 'awesome'
}
]
}
}
The shuffleArray is just to show that the records could be in any order, and the id (source_id) property is not necessarily in incremental order (actually in my case they are UUIDs with the hierarchy not really in any particular order). Each "record" in buildTree is a "property" record basically like this:
create table object_properties {
uuid id;
uuid source_id; // the object which has this property
string name; // the property name
uuid value; // the property value object
}
// ...and same for boolean, integer, etc. properties
create table string_properties {
uuid id;
uuid source_id; // the object which has this property
string name; // the property name
string value; // the property value string
}
In my buildTree I can kind of imagine creating a map from the source_id (the base object node which has property name), to the names, to the values. But then maybe iterating over the source IDs, looking for objects nested inside the name values, and converting them to objects instead of just IDs. But this is getting hard to comprehend and I'm sure there is an easier way somehow.
What is an algorithm to build an "object tree" from this flat list of records?
In my situation, I am fetching a bunch of deeply nested property objects, recursively, and need to stitch back together an object tree out of them.
It looks like the name "nested" plays a special role. When it occurs, the corresponding value property does not hold a literal value to assign to the named property (as is the case with other names), but is a reference to an existing source_id value.
This means your code needs to deal with that name specifically and then establish the parent-child relationship. This relationship is further influenced by the array property.
I would define buildTree as follows, making use of a Map, which is built first using its constructor argument:
function buildTree(startId, arr) {
const map = new Map(arr.map(({source_id}) => [source_id, { id: source_id }]));
for (const {source_id, name, value, array} of arr) {
if (name !== "nested") {
map.get(source_id)[name] = value;
} else if (array) {
(map.get(source_id).nested ??= []).push(map.get(value));
} else {
map.get(source_id).nested = map.get(value);
}
}
return map.get(startId);
}
// Code below has not changed
function shuffleArray(array) { for (var i = array.length - 1, j, temp; i > 0; i--) {j = Math.floor(Math.random() * (i + 1));temp = array[i];array[i] = array[j];array[j] = temp;} return array;}
const tree = buildTree(1, shuffleArray([{ type: 'string', source_id: 1, name: 'foo', value: 'asdf' },{ type: 'integer', source_id: 1, name: 'bar', value: 123 },{ type: 'object', source_id: 1, name: 'nested', value: 2 },{ type: 'object', source_id: 2, name: 'nested', value: 3, array: true },{ type: 'boolean', source_id: 3, name: 'random', value: true },{ type: 'string', source_id: 3, name: 'another', value: 'hello' },{ type: 'object', source_id: 2, name: 'nested', value: 4, array: true },{ type: 'boolean', source_id: 4, name: 'random', value: false },{ type: 'string', source_id: 4, name: 'another', value: 'world' },{ type: 'object', source_id: 2, name: 'nested', value: 5, array: true },{ type: 'boolean', source_id: 5, name: 'random', value: true },{ type: 'string', source_id: 5, name: 'another', value: 'awesome' },]))
console.log(tree);
Note that the order in which objects are pushed into arrays is defined by the original order of the objects. Since this input array is shuffled, the output may show arrays in different ordering on separate runs. Something similar holds for object keys (see Object property order)
You should try Array.prototype.group(). Please refer below document.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/group
const inventory = [
{ name: 'asparagus', type: 'vegetables', quantity: 5 },
{ name: 'bananas', type: 'fruit', quantity: 0 },
{ name: 'goat', type: 'meat', quantity: 23 },
{ name: 'cherries', type: 'fruit', quantity: 5 },
{ name: 'fish', type: 'meat', quantity: 22 }
];
const result = inventory.group(({ type }) => type);
/* Result is:
{
vegetables: [
{ name: 'asparagus', type: 'vegetables', quantity: 5 },
],
fruit: [
{ name: "bananas", type: "fruit", quantity: 0 },
{ name: "cherries", type: "fruit", quantity: 5 }
],
meat: [
{ name: "goat", type: "meat", quantity: 23 },
{ name: "fish", type: "meat", quantity: 22 }
]
}
*/

How to filter nested arrays by searching

I have an array of objects that I want to filter by comparing a nested property to a search term.
For example:
let array = [
{
category: 15,
label: "Components",
value: "a614741f-7d4b-4b33-91b7-89a0ef96a099",
children: [
{
category: 1,
label: "Carousel1",
diId: 55946,
// as you can see there are many children nested array of object
children: [{ label: "nodatafoundmessage", value: "47d18fb2-3e63-4542-ad0e-e5e09acb5016", children: [] }],
value: "be5e027b-9163-4cfb-8816-0c8e3b816086"
},
{
category: 2,
label: "Checkbox1",
diId: 193909,
children: [{ label: "datafound", value: "47d18sb2-3e63-4542-ad0e-e5e09acb5016", children: [] }],
value: "045e8786-2165-4e1e-a839-99b1b0ceef57"
}
]
},
{
value: "4be22726-850c-4905-ab3b-039fcf607d55",
label: "Default",
children: [
{
category: 5,
defaultValueType: 1,
label: "Empty",
toType: "String",
value: "ebedb43f-4c53-491f-8954-d030321845cd"
},
{
category: 5,
defaultValueType: 2,
label: "Space",
toType: "String",
value: "2d0e1429-572b-4f21-9f83-3340bafff95a"
},
{
category: 5,
defaultValueType: 8,
label: "Current Username",
toType: "String",
value: "25f6b40a-33c7-4f17-b29d-99e8d1e4e33c"
},
{
category: 5,
defaultValueType: 9,
label: "Current Location",
toType: "Location",
value: "ed59da2f-318d-4599-9085-4d9d769a27d7"
}
]
},
{
category: 4,
label: "Fixed Value",
isFixed: true,
value: "28e90e3e-a20b-4499-9593-061a7d1e7bd6"
// as you can see there is no children in this object
}
]};
What I'm trying to achieve is if I search for 'nodata' for example my result should be
let array = [
{
category: 15,
label: "Components",
value: "a614741f-7d4b-4b33-91b7-89a0ef96a099",
children: [
{
category: 1,
label: "Carousel1",
diId: 55946,
// as you can see there are many children nested array of object
children: [{ label: "nodatafoundmessage", value: "47d18fb2-3e63-4542-ad0e-e5e09acb5016", children: [] }],
value: "be5e027b-9163-4cfb-8816-0c8e3b816086"
}
]
}
];
Another option if I search for 'spa' my result should be
let array = [
{
value: "4be22726-850c-4905-ab3b-039fcf607d55",
label: "Default",
children: [
{
category: 5,
defaultValueType: 2,
label: "Space",
toType: "String",
value: "2d0e1429-572b-4f21-9f83-3340bafff95a"
}
]
}
];
I have been super confused and I decided to get some help. Thank you for your helps guys!
The following function should do the trick for you:
function searchData(dataArray, searchTerm) {
return dataArray.flatMap(obj => {
const objHasSearchTerm = Object.entries(obj)
.some(([key, value]) => key !== 'children' && String(value).toLowerCase().includes(searchTerm.toLowerCase()));
if (objHasSearchTerm && !obj.children) return [obj];
const matchedChildren = searchData(obj.children ?? [], searchTerm);
return objHasSearchTerm || matchedChildren.length > 0
? [{
...obj,
children: matchedChildren,
}]
: [];
})
}
It recursively goes through the data array, looks for any entries that have the specified search term, and if so, places it into the newly constructed object. It will preserve the nested shape of the object, which may or may not be what is needed. Feel free to tweak the algorithm to your own needs.
let allData = [
{
category: 15,
label: "Components",
value: "a614741f-7d4b-4b33-91b7-89a0ef96a099",
children: [
{
category: 1,
label: "Carousel1",
diId: 55946,
// as you can see there are many children nested array of object
children: [{ label: "nodatafoundmessage", value: "47d18fb2-3e63-4542-ad0e-e5e09acb5016", children: [] }],
value: "be5e027b-9163-4cfb-8816-0c8e3b816086"
},
{
category: 2,
label: "Checkbox1",
diId: 193909,
children: [{ label: "datafound", value: "47d18sb2-3e63-4542-ad0e-e5e09acb5016", children: [] }],
value: "045e8786-2165-4e1e-a839-99b1b0ceef57"
}
]
},
{
value: "4be22726-850c-4905-ab3b-039fcf607d55",
label: "Default",
children: [
{
category: 5,
defaultValueType: 1,
label: "Empty",
toType: "String",
value: "ebedb43f-4c53-491f-8954-d030321845cd"
},
{
category: 5,
defaultValueType: 2,
label: "Space",
toType: "String",
value: "2d0e1429-572b-4f21-9f83-3340bafff95a"
},
{
category: 5,
defaultValueType: 8,
label: "Current Username",
toType: "String",
value: "25f6b40a-33c7-4f17-b29d-99e8d1e4e33c"
},
{
category: 5,
defaultValueType: 9,
label: "Current Location",
toType: "Location",
value: "ed59da2f-318d-4599-9085-4d9d769a27d7"
}
]
},
{
category: 4,
label: "Fixed Value",
isFixed: true,
value: "28e90e3e-a20b-4499-9593-061a7d1e7bd6"
// as you can see there is no children in this object
}
];
function searchData(dataArray, searchTerm) {
return dataArray.flatMap(obj => {
const objHasSearchTerm = Object.entries(obj)
.some(([key, value]) => key !== 'children' && String(value).toLowerCase().includes(searchTerm.toLowerCase()));
if (objHasSearchTerm && !obj.children) return [obj];
const matchedChildren = searchData(obj.children ?? [], searchTerm);
return objHasSearchTerm || matchedChildren.length > 0
? [{
...obj,
children: matchedChildren,
}]
: [];
})
}
console.log('----- Search: nodata')
console.log(JSON.stringify(searchData(allData, 'nodata'), null, 2))
console.log('----- Search: spa')
console.log(JSON.stringify(searchData(allData, 'spa'), null, 2))

Array.sort() only for elements that have a specific key

I have an array like:
let arr = [
{
label: "abc",
value: 2,
checked: true
},
{
label: "bcd",
value: 1,
checked: true
},
{
label: "cde",
value: 4,
checked: false
},{
label: "def",
value: 6,
checked: true
},
{
label: "efg",
value: 3,
checked: false
},
{
label: "fgh",
value: 5,
checked: true
}
]
I'm trying to sort this in two different ways ( alphabetically, by value ). The current implementation of the sort works properly, but I'm running into some issues when I'm sorting by value... How can I obtain an array of these values from 'arr' sorted descending by value only when the checked flag is true? I mean... I want to sort by value only the elements that are checked and for the rest of the elements to preserve them as they are at the initial index...
//expected result
let arr = [
{ //sorted index 0
label: "bcd",
value: 1,
checked: true
},
{ //sorted index 1
label: "abc",
value: 2,
checked: true
},
{ //checked: false - perserve index (2) from the initial array
label: "cde",
value: 4,
checked: false
},
{ //sorted index 3
label: "fgh",
value: 5,
checked: true
},
{ //checked: false - perserve index (4) from the initial array
label: "efg",
value: 3,
checked: false
},
{ //sorted index 5
label: "def",
value: 6,
checked: true
}
]
Is there a simple way to use lodash? Or is it needed to do it manually, and how?
This solution works by copying the checked items into a separate array, sorting them, then reinserting them into (a copy of) the original array.
const
// Gets the original array
arr = getArr()
// Defines a function to use for sorting
compare = (a, b) => a.value - b.value,
// Makes an array of just the objects where `.checked` is truthy, and sorts it
partSorted = arr
.filter(obj => obj.checked)
.sort(compare);
// Defines a variable to track the index within the `partSorted` array
let i = 0;
// Copies `arr`, but substitutes the next item from `partSorted` if appropriate
const sorted = arr.map(obj =>
obj.checked ? partSorted[i++] : obj
);
// Shows the result
console.log(sorted);
// Defines the original array
function getArr(){
return [
{ label: "abc", value: 2, checked: true },
{ label: "bcd", value: 1, checked: true },
{ label: "cde", value: 4, checked: false },
{ label: "def", value: 6, checked: true },
{ label: "efg", value: 3, checked: false },
{ label: "fgh", value: 5, checked: true }
];
}
You could store the indices to put the sorted values back and take the objects for sorting. Sort and apply back.
var array = [{ label: "abc", value: 2, checked: true }, { label: "bcd", value: 1, checked: true }, { label: "cde", value: 4, checked: false }, { label: "def", value: 6, checked: true }, { label: "efg", value: 3, checked: false }, { label: "fgh", value: 5, checked: true }],
indices = [];
array
.filter(({ checked }, i) => checked && indices.push(i))
.sort(({ value: a }, { value: b }) => a - b)
.forEach((v, i) => array[indices[i]] = v);
console.log(array);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can use native JavaScript sort. 1 and -1 changes the sort position, 0 will keep the order.
If it is reversed then change a with b.
arr.sort((a, b) => a.checked ? b.value - a.value : 0);
I know I am late but here is a different appreach using bubbling
for(let i=0;i<arr1.length-1;i++){
for(let j=0;j<arr1.length-i-1;j++){
if(arr1[j].checked==true){
if(arr1[j].value>arr1[j+1].value){
v=arr1[j]
arr1[j]=arr1[j+1]
arr1[j+1]=v
}
}
}
}

Moving data from 2 arrays of objects into a third array

so assume i have 2 arrays of objects...
let orders = [
{ id: 1, itemName: 'Peaches', amount: 2 },
{ id: 2, itemName: 'Mangoes', amount: 1 },
{ id: 3, itemName: 'Mangoes', amount: 10 }
];
let items = [
{ id: 1, name: 'Peaches', qty: 10 },
{ id: 2, name: 'Mangoes', qty: 3 }
];
and i want to find the list of orders for every item and put them in an array called linkedOrders, I tried the below code:
let linkedOrders = _.map(items, item => _.where(orders, { name: item.name }));
console.log(linkedOrders);
This is what I am getting:
[{ id: 1, itemName: 'Peaches', amount: 2 }],
[{ id: 2, itemName: 'Mangoes', amount: 1 },
{ id: 3, itemName: 'Mangoes', amount: 10 }]
but I want something like this:
[{'Peaches': [
{ id: 1, itemName: 'Peaches', amount: 2 }
],
'Mangoes': [
{ id: 2, itemName: 'Mangoes', amount: 1 },
{ id: 3, itemName: 'Mangoes', amount: 10 }
]],
Any help would be appreciated thanks.
You can simply do that using Array reduce method.
const result = items.reduce((result, item) => {
result.push({
[item.name]: orders.filter((order) => order.itemName === item.name)
});
return result;
}, []);
For more information on reduce, check Array​.prototype​.reduce()

Filter and combine fields from 2 arrays with ES6 JavaScript?

I have 2 arrays, one of pizza details and the other is an order state. The id field is what links them. So in this example, the order has 2 x Pepperoni and 3 x Margarita.
const pizzaContent = [
{
id: 0,
name: 'Pepperoni',
price: 20,
hot: true,
stockQuantity: 3
},
{
id: 1,
name: 'Margarita',
price: 25,
stockQuantity: 3
},
{
id: 2,
name: 'Hawaiian',
price: 15,
stockQuantity: 0
}
];
const orders = [{
id: 0,
quantity: 2
},{
id: 1,
quantity: 3
}];
I'm trying to create a new array which has the quantity from orders and the fields from pizzaContent. Any pizzas which aren't in the order shouldn't be part of this array.
I've gotten close with the following:
const pizzasInOrder = this.props.orders.map(order => {
return (
{
quantity: order.quantity,
pizza: this.props.pizzas.find(pizza => {
return (
order.id === pizza.id
);
})
}
)
});
However, the result is:
pizzasInOrder = [
{
pizza: {id: 0, name: "Pepperoni", price: 20, hot: true, stockQuantity: 3},
quantity:2
},
{
pizza: {id: 1, name: "Margarita", price: 25, stockQuantity: 3},
quantity:3
}
]
But what I need is:
pizzasInOrder = [
{
id: 0, name: "Pepperoni", price: 20, hot: true, stockQuantity: 3, quantity: 2
},
{
id: 1, name: "Margarita", price: 25, stockQuantity: 3, quantity: 3
}
]
Use Object.assign and no extra keys
const pizzasInOrder = this.props.orders.map(order =>
Object.assign({quantity: order.quantity},
this.props.pizzas.find(pizza => order.id === pizza.id))
);
You can use Object.assign() to merge objects into one.
example..
const pizzaContent = [
{
id: 0,
name: 'Peperoni',
price: 20,
hot: true,
stockQuantity: 3
},
{
id: 1,
name: 'Margarita',
price: 25,
stockQuantity: 3
},
{
id: 2,
name: 'Hawian',
price: 15,
stockQuantity: 0
}
];
const orders = [{
id: 0,
quantity: 2
},{
id: 1,
quantity: 3
}];
let pizzasInOrder = orders.map((order) => {
return Object.assign(order,
pizzaContent.find(pizza => order.id === pizza.id));
});
console.log(pizzasInOrder);

Categories