编写一个函数对对象数组进行排序(通过使用另一个对象来指定排序路径和顺序)

Posted

技术标签:

【中文标题】编写一个函数对对象数组进行排序(通过使用另一个对象来指定排序路径和顺序)【英文标题】:Write a function to sort an array of objects (by using another object to specify the sorting path and order ) 【发布时间】:2019-09-18 16:39:17 【问题描述】:

我将编写一个函数来对任何具有某种结构的 JSON 进行排序(只要知道它是一个对象数组,例如产品列表),方法是使用另一个对象作为其参数来确定根据哪个对象执行哪些排序键。

// The json that I get might looks like something like this.
// I just write one item of the array, but all of them are the same.
// But the blueprint of the items in each json are different.
const dataArray = [
  
    id: 100,
    name: 'product_1',
    price: 99.95,
    color: ['#fff', '#f0f', '#00f'],
    category: ['cat_1', 'cat_2'],
    booleanKey: true,
    foo: 
      foo1: 12,
      foo2: 'string value',
      foo3: 
        foo3_1: 'string value',
        foo3_2: 732.342
      ,
      foo4: [
        
          foo4_1_1: 853,
          foo4_1_2: 24,
        ,
        
          foo4_2_1: 'string value',
          foo4_2_2: 435,
          foo4_2_3: 25.35,
        
      ]
    ,
    bar: 
      bar1: 
        bar1_1: 313,
        bar1_2: 32.73
      ,
      bar2: 75.3
    
  
];

// The function to sort the array
const sortData = obj => 
  return dataArray.sort((a, b) => 
    // Do something to return the correct value
  );


// Sort array ascending by value 1, and descending by value -1
const result = sortData(foo: foo3: foo3_2: 1);
const anotherResult = sortData(bar: bar2: -1);
const anotherResult = sortData(price: 1);

因为我必须编写一个函数来对具有任何结构的任何项目进行排序,所以我必须使用它们传递给我的 obj 作为参数来根据该对象的键对数组进行排序。

我找到了这样的解决方案:

// The sample array that be used from the other answer
const fooArray = [
   foo1:  foo2:  foo3: 123   ,
   foo1:  foo2:  foo3: 987   ,
   foo1:  foo2:  foo3: 456   ,
   foo1:  foo2:  foo3: 789   ,
   foo1:  foo2:  foo3: 321   ,
   foo1:  foo2:  foo3: 654   
];
const sortData = obj => 
  const pathArray = [];
  while(typeof(obj) == 'object') 
    pathArray.push(Object.keys(obj)[0]);
    obj = obj[pathArray[pathArray.length - 1]];
  
  const order = obj;
  return fooArray.sort((a, b) => 
    for(let pathKey of pathArray) 
      a = a[pathKey];
      b = b[pathKey];
    
    return (a - b) * order;
  );

const foo = sortData( foo1:  foo2:  foo3: 1   );

还有其他建议或想法吗?

【问题讨论】:

类似于mongodb的排序实现。 mongo 的管道排序阶段的参数正是您的设计。 请也分享预期的输出 【参考方案1】:

使用Sort array of objects by string property value作为参考实现,通过任意选择的字符串或对象的编号进行排序,我们只需要使用下面的criteria()函数实现对象到选择器的转换:

const criteria = (o, select = v => v) => 
  const [[key, order]] = Object.entries(o)
  const fn = v => select(v)[key]
  return typeof order === 'object'
    ? criteria(order, fn)
    :  order, fn 


const sortData = o => (( order, fn ) =>
  (a, b) => 
    const fa = fn(a)
    const fb = fn(b)
    return order * (-(fa < fb) || +(fa > fb))
  
)(criteria(o))

const foo = sortData( foo1:  foo2:  foo3: 1   )
const fooArray = [
   foo1:  foo2:  foo3: 123   ,
   foo1:  foo2:  foo3: 987   ,
   foo1:  foo2:  foo3: 456   ,
   foo1:  foo2:  foo3: 789   ,
   foo1:  foo2:  foo3: 321   ,
   foo1:  foo2:  foo3: 654   
]

console.log(fooArray.sort(foo))

const bar = sortData( bar1:  bar2:  bar3: -1   )
const barArray = [
   bar1:  bar2:  bar3: 'abc'   ,
   bar1:  bar2:  bar3: 'ihg'   ,
   bar1:  bar2:  bar3: 'def'   ,
   bar1:  bar2:  bar3: 'ghi'   ,
   bar1:  bar2:  bar3: 'cba'   ,
   bar1:  bar2:  bar3: 'fed'   
]

console.log(barArray.sort(bar))
.as-console-wrappermin-height:100%!important

【讨论】:

谢谢兄弟。你的代码很棒,但我认为理解起来有点复杂(也许对我来说)。 @Bawbak 如果您能指出您难以掌握的具体部分,我很乐意详细说明。【参考方案2】:

对于排序逻辑,很简单:

const sortData = path => 
  const sortOrder = query(path, path)

  return dataArray.sort((a, b) => 
    const valueOfA = query(a, path)
    const valueOfB = query(b, path)

    if (valueOfA < valueOfB) return -sortOrder
    if (valueOfA > valueOfB) return sortOrder
    return 0
  )

这里唯一复杂的是如何从另一个对象类型的参数中查询排序路径和顺序。

所以我在这里定义了一个辅助函数来完成这项工作:

function query (target, path) 
  const [key] = Object.keys(path)

  if (typeof path[key] !== 'object') 
    return target[key]
  

  return query(path[key], target)

你可以在这里测试一下:

let dataArray = [id: 100, id: 200]

const sortData = path => 
  const sortOrder = query(path, path)

  return dataArray.sort((a, b) => 
    const valueOfA = query(a, path)
    const valueOfB = query(b, path)

    if (valueOfA < valueOfB) return -sortOrder
    if (valueOfA > valueOfB) return sortOrder
    return 0
  )


console.log(sortData(id: 1))        // 100, 200
console.log(sortData(id: -1))       // 200, 100

dataArray = [
  foo: bar: 'bar', a:'a', b:'b',
  foo: bar: 'car', a:'A', b:'B'
]

console.log(sortData(foo: bar: 1))     // [...bar, ...car]
console.log(sortData(foo: bar: -1))    // [...car, ...bar]

function query (target, path) 
  const [key] = Object.keys(path)

  if (typeof path[key] !== 'object') 
    return target[key]
  

  return query(path[key], target)

【讨论】:

是的,请继续,我从您的编辑中学到了很多,谢谢! @PatrickRoberts 干得好。您的代码与我所做的非常相似(但不完全)。但我想知道这可能是一个更优化和更短的解决方案,或者用 es6 的东西做一些魔法:)。

以上是关于编写一个函数对对象数组进行排序(通过使用另一个对象来指定排序路径和顺序)的主要内容,如果未能解决你的问题,请参考以下文章

根据另一个 id 数组对对象数组进行排序

我们如何根据节点js中的另一个对象数组值对对象数组进行排序

如何使用对象的键对对象数组进行排序? [复制]

如何根据另一个数组的顺序对对象数组进行排序?

Java按整数字段对对象数组进行排序,如果它们相同,则按另一个整数值排序

使用c ++中对象中的函数的结果对对象数组进行排序